diff --git a/BBSUtilities.c b/BBSUtilities.c index 487b6a9..1d82b8c 100644 --- a/BBSUtilities.c +++ b/BBSUtilities.c @@ -9668,7 +9668,7 @@ VOID SaveConfig(char * ConfigName) // Save UI config - for (i=1; i<=32; i++) + for (i=1; i <= GetNumberofPorts(); i++) { char Key[100]; @@ -10192,7 +10192,7 @@ BOOL GetConfig(char * ConfigName) GetStringValue(group, "Version", Size); sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); - for (i=1; i<=32; i++) + for (i =1 ; i <= GetNumberofPorts(); i++) { char Key[100]; @@ -10654,7 +10654,8 @@ int Disconnected (int Stream) } /* ---- TAJ PG SERVER ---- */ - if ( conn->UserPointer->Temp->RUNPGPARAMS ) { + if (conn->UserPointer && conn->UserPointer->Temp && conn->UserPointer->Temp->RUNPGPARAMS) + { printf("Freeing RUNPGPARAMS\n"); free(conn->UserPointer->Temp->RUNPGPARAMS); conn->UserPointer->Temp->RUNPGPARAMS = NULL; diff --git a/BBSUtilities.c.bak b/BBSUtilities.c.bak index b60c77d..c5c7e48 100644 --- a/BBSUtilities.c.bak +++ b/BBSUtilities.c.bak @@ -1,14926 +1,14926 @@ -/* -Copyright 2001-2018 John Wiseman G8BPQ - -This file is part of LinBPQ/BPQ32. - -LinBPQ/BPQ32 is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -LinBPQ/BPQ32 is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses -*/ - -// Mail and Chat Server for BPQ32 Packet Switch -// -// Utility Routines - -#include "bpqmail.h" -#ifdef WIN32 -#include "Winspool.h" -#else -#include -#endif - - -BOOL Bells; -BOOL FlashOnBell; // Flash instead of Beep -BOOL StripLF; - -BOOL WarnWrap; -BOOL FlashOnConnect; -BOOL WrapInput; -BOOL CloseWindowOnBye; - -RECT ConsoleRect; - -BOOL OpenConsole; -BOOL OpenMon; - -int reportNewMesageEvents = 0; - - -extern struct ConsoleInfo BBSConsole; - -extern char LOC[7]; - -//#define BBSIDLETIME 120 -//#define USERIDLETIME 300 - - -#define BBSIDLETIME 900 -#define USERIDLETIME 900 - -#ifdef LINBPQ -extern BPQVECSTRUC ** BPQHOSTVECPTR; -UCHAR * GetLogDirectory(); -DllExport int APIENTRY SessionStateNoAck(int stream, int * state); -int RefreshWebMailIndex(); -#else -__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; -typedef char * (WINAPI FAR *FARPROCZ)(); -typedef int (WINAPI FAR *FARPROCX)(); -FARPROCZ pGetLOC; -FARPROCX pRefreshWebMailIndex; - -#endif - -Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); -VOID APIENTRY md5 (char *arg, unsigned char * checksum); -int APIENTRY GetRaw(int stream, char * msg, int * len, int * count); -void GetSemaphore(struct SEM * Semaphore, int ID); -void FreeSemaphore(struct SEM * Semaphore); -int EncryptPass(char * Pass, char * Encrypt); -VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); -void DeletetoRecycle(char * FN); -VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID TidyPrompts(); -char * ReadMessageFileEx(struct MsgInfo * MsgRec); -char * APIENTRY GetBPQDirectory(); -BOOL SendARQMail(CIRCUIT * conn); -int APIENTRY ChangeSessionIdletime(int Stream, int idletime); -int APIENTRY GetApplNum(int Stream); -VOID FormatTime(char * Time, time_t cTime); -BOOL CheckifPacket(char * Via); -char * APIENTRY GetVersionString(); -void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename); -void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); -int GetCMSHash(char * Challenge, char * Password); -BOOL SendAMPRSMTP(CIRCUIT * conn); -VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen); -VOID MCastTimer(); -VOID MCastConTimer(ConnectionInfo * conn); -int FindFreeBBSNumber(); -VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -BOOL ProcessYAPPMessage(CIRCUIT * conn); -void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); -void YAPPSendData(ConnectionInfo * conn); -VOID CheckBBSNumber(int i); -struct UserInfo * FindAMPR(); -VOID SaveInt64Value(config_setting_t * group, char * name, long long value); -VOID SaveIntValue(config_setting_t * group, char * name, int value); -VOID SaveStringValue(config_setting_t * group, char * name, char * value); -char *stristr (char *ch1, char *ch2); -BOOL CheckforMessagetoServer(struct MsgInfo * Msg); -void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled); -void ListCategories(ConnectionInfo * conn); -void RebuildNNTPList(); -long long GetInt64Value(config_setting_t * group, char * name); -void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); -int ReformatSyncMessage(CIRCUIT * conn); -char * initMultipartUnpack(char ** Input); -char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg); -int decode_quoted_printable(char *ptr, int len); -void decodeblock( unsigned char in[4], unsigned char out[3]); -int encode_quoted_printable(char *s, char * out, int Len); -int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); -int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); - -config_t cfg; -config_setting_t * group; - -extern ULONG BBSApplMask; - -//static int SEMCLASHES = 0; - -char SecureMsg[80] = ""; // CMS Secure Signon Response - -int NumberofStreams; - -extern char VersionStringWithBuild[50]; - -#define MaxSockets 64 - -extern struct SEM OutputSEM; - -extern ConnectionInfo Connections[MaxSockets+1]; - -extern struct UserInfo ** UserRecPtr; -extern int NumberofUsers; - -extern struct UserInfo * BBSChain; // Chain of users that are BBSes - -extern struct MsgInfo ** MsgHddrPtr; -extern int NumberofMessages; - -extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search - -extern char UserDatabaseName[MAX_PATH]; -extern char UserDatabasePath[MAX_PATH]; - -extern char MsgDatabasePath[MAX_PATH]; -extern char MsgDatabaseName[MAX_PATH]; - -extern char BIDDatabasePath[MAX_PATH]; -extern char BIDDatabaseName[MAX_PATH]; - -extern char WPDatabasePath[MAX_PATH]; -extern char WPDatabaseName[MAX_PATH]; - -extern char BadWordsPath[MAX_PATH]; -extern char BadWordsName[MAX_PATH]; - -extern char BaseDir[MAX_PATH]; -extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% -extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat - - -extern char MailDir[MAX_PATH]; - -extern BIDRec ** BIDRecPtr; -extern int NumberofBIDs; - -extern BIDRec ** TempBIDRecPtr; -extern int NumberofTempBIDs; - -extern WPRec ** WPRecPtr; -extern int NumberofWPrecs; - -extern char ** BadWords; -extern int NumberofBadWords; -extern char * BadFile; - -extern int LatestMsg; -extern struct SEM MsgNoSemaphore; // For locking updates to LatestMsg -extern int HighestBBSNumber; - -extern int MaxMsgno; -extern int BidLifetime; -extern int MaxAge; -extern int MaintInterval; -extern int MaintTime; - -extern int ProgramErrors; - -extern BOOL MonBBS; -extern BOOL MonCHAT; -extern BOOL MonTCP; - -BOOL SendNewUserMessage = TRUE; -BOOL AllowAnon = FALSE; -BOOL UserCantKillT = FALSE; - -typedef int (WINAPI FAR *FARPROCX)(); -FARPROCX pRunEventProgram; - -int RunEventProgram(char * Program, char * Param); - - -extern BOOL EventsEnabled; - -#define BPQHOSTSTREAMS 64 - -// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 - -extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; - -#ifdef LINBPQ -extern BPQVECSTRUC ** BPQHOSTVECPTR; -extern char WL2KModes [54][18]; -#else -__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; - - -char WL2KModes [54][18] = { - "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", - "", "Pactor 1", "", "", "Pactor 2", "", "Pactor 3", "", "", "Pactor 4", // 10 - 20 - "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 - "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 - "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 - "VARA", "VARA FM", "VARA FM WIDE", "VARA 500"}; -#endif - - - - - -FILE * LogHandle[4] = {NULL, NULL, NULL, NULL}; - -time_t LastLogTime[4] = {0, 0, 0, 0}; - -char FilesNames[4][100] = {"", "", "", ""}; - -char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; - - -BOOL OpenLogfile(int Flags) -{ - UCHAR FN[MAX_PATH]; - time_t LT; - struct tm * tm; - - LT = time(NULL); - tm = gmtime(<); - - sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); - - LogHandle[Flags] = fopen(FN, "ab"); - -#ifndef WIN32 - - if (strcmp(FN, &FilesNames[Flags][0])) - { - UCHAR SYMLINK[MAX_PATH]; - - sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); - unlink(SYMLINK); - strcpy(&FilesNames[Flags][0], FN); - symlink(FN, SYMLINK); - } - -#endif - - return (LogHandle[Flags] != NULL); -} - -typedef int (WINAPI FAR *FARPROCX)(); - -extern FARPROCX pDllBPQTRACE; - -struct SEM LogSEM = {0, 0}; - -void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) -{ - char CRLF[2] = {0x0d,0x0a}; - struct tm * tm; - char Stamp[20]; - time_t LT; -// struct _EXCEPTION_POINTERS exinfo; - - // Write to Node BPQTRACE system - - if ((Flags == LOG_BBS || Flags == LOG_DEBUG_X) && MsgLen < 250) - { - MESSAGE Monframe; - memset(&Monframe, 0, sizeof(Monframe)); - - Monframe.PORT = 64; - Monframe.LENGTH = 12 + MsgLen; - Monframe.DEST[0] = 1; // Plain Text Monitor - - memcpy(&Monframe.DEST[1], Msg, MsgLen); - Monframe.DEST[1 + MsgLen] = 0; - - time(&Monframe.Timestamp); -#ifdef LINBPQ - GetSemaphore(&Semaphore, 88); - BPQTRACE(&Monframe, FALSE); - FreeSemaphore(&Semaphore); -#else - if (pDllBPQTRACE) - pDllBPQTRACE(&Monframe, FALSE); -#endif - } -#ifndef LINBPQ - __try - { -#endif - - - -#ifndef LINBPQ - - if (hMonitor) - { - if (Flags == LOG_TCP && MonTCP) - { - WritetoMonitorWindow((char *)&Flag, 1); - WritetoMonitorWindow(Msg, MsgLen); - WritetoMonitorWindow(CRLF , 1); - } - else if (Flags == LOG_CHAT && MonCHAT) - { - WritetoMonitorWindow((char *)&Flag, 1); - - if (conn && conn->Callsign[0]) - { - char call[20]; - sprintf(call, "%s ", conn->Callsign); - WritetoMonitorWindow(call, 10); - } - else - WritetoMonitorWindow(" ", 10); - - WritetoMonitorWindow(Msg, MsgLen); - if (Msg[MsgLen-1] != '\r') - WritetoMonitorWindow(CRLF , 1); - } - else if (Flags == LOG_BBS && MonBBS) - { - WritetoMonitorWindow((char *)&Flag, 1); - if (conn && conn->Callsign[0]) - { - char call[20]; - sprintf(call, "%s ", conn->Callsign); - WritetoMonitorWindow(call, 10); - } - else - WritetoMonitorWindow(" ", 10); - - WritetoMonitorWindow(Msg, MsgLen); - WritetoMonitorWindow(CRLF , 1); - } - else if (Flags == LOG_DEBUG_X) - { - WritetoMonitorWindow((char *)&Flag, 1); - WritetoMonitorWindow(Msg, MsgLen); - WritetoMonitorWindow(CRLF , 1); - } - } -#endif - - if (Flags == LOG_TCP && !LogTCP) - return; - if (Flags == LOG_BBS && !LogBBS) - return; - if (Flags == LOG_CHAT && !LogCHAT) - return; - - GetSemaphore(&LogSEM, 0); - - if (LogHandle[Flags] == NULL) - OpenLogfile(Flags); - - if (LogHandle[Flags] == NULL) - { - FreeSemaphore(&LogSEM); - return; - } - LT = time(NULL); - tm = gmtime(<); - - sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); - - fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); - - if (conn && conn->Callsign[0]) - { - char call[20]; - sprintf(call, "%s ", conn->Callsign); - fwrite(call, 1, 10, LogHandle[Flags]); - } - else - fwrite(" ", 1, 10, LogHandle[Flags]); - - fwrite(Msg, 1, MsgLen, LogHandle[Flags]); - - if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') - fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); - else - fwrite(CRLF, 1, 2, LogHandle[Flags]); - - // Don't close/reopen logs every time - -// if ((LT - LastLogTime[Flags]) > 60) - { - LastLogTime[Flags] = LT; - fclose(LogHandle[Flags]); - LogHandle[Flags] = NULL; - } - FreeSemaphore(&LogSEM); - -#ifndef LINBPQ - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } -#endif -} - -int CriticalErrorHandler(char * error) -{ - Debugprintf("Critical Error %s", error); - ProgramErrors = 25; - CheckProgramErrors(); // Force close - return 0; -} - -BOOL CheckForTooManyErrors(ConnectionInfo * conn) -{ - conn->ErrorCount++; - - if (conn->ErrorCount > 4) - { - BBSputs(conn, "Too many errors - closing\r"); - conn->CloseAfterFlush = 20; - return TRUE; - } - return FALSE; -} - - - - - -VOID __cdecl Debugprintf(const char * format, ...) -{ - char Mess[16384]; - va_list(arglist); - int Len; - - va_start(arglist, format); - Len = vsprintf(Mess, format, arglist); -#ifndef LINBPQ - WriteLogLine(NULL, '!',Mess, Len, LOG_DEBUG_X); -#endif - // #ifdef _DEBUG - strcat(Mess, "\r\n"); - OutputDebugString(Mess); - -// #endif - return; -} - -VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...) -{ - char Mess[1000]; - va_list(arglist);int Len; - - va_start(arglist, format); - Len = vsprintf(Mess, format, arglist); - WriteLogLine(conn, InOut, Mess, Len, LogMode); - - return; -} - -struct MsgInfo * GetMsgFromNumber(int msgno) -{ - if (msgno < 1 || msgno > 999999) - return NULL; - - return MsgnotoMsg[msgno]; -} - -struct UserInfo * AllocateUserRecord(char * Call) -{ - struct UserInfo * User = zalloc(sizeof (struct UserInfo)); - - strcpy(User->Call, Call); - User->Length = sizeof (struct UserInfo); - - GetSemaphore(&AllocSemaphore, 0); - - UserRecPtr=realloc(UserRecPtr,(++NumberofUsers+1) * sizeof(void *)); - UserRecPtr[NumberofUsers]= User; - - FreeSemaphore(&AllocSemaphore); - - return User; -} - -struct MsgInfo * AllocateMsgRecord() -{ - struct MsgInfo * Msg = zalloc(sizeof (struct MsgInfo)); - - GetSemaphore(&AllocSemaphore, 0); - - MsgHddrPtr=realloc(MsgHddrPtr,(++NumberofMessages+1) * sizeof(void *)); - MsgHddrPtr[NumberofMessages] = Msg; - - FreeSemaphore(&AllocSemaphore); - - return Msg; -} - -BIDRec * AllocateBIDRecord() -{ - BIDRec * BID = zalloc(sizeof (BIDRec)); - - GetSemaphore(&AllocSemaphore, 0); - - BIDRecPtr = realloc(BIDRecPtr,(++NumberofBIDs+1) * sizeof(void *)); - BIDRecPtr[NumberofBIDs] = BID; - - FreeSemaphore(&AllocSemaphore); - - return BID; -} - -BIDRec * AllocateTempBIDRecord() -{ - BIDRec * BID = zalloc(sizeof (BIDRec)); - - GetSemaphore(&AllocSemaphore, 0); - - TempBIDRecPtr=realloc(TempBIDRecPtr,(++NumberofTempBIDs+1) * sizeof(void *)); - TempBIDRecPtr[NumberofTempBIDs] = BID; - - FreeSemaphore(&AllocSemaphore); - - return BID; -} - -struct UserInfo * LookupCall(char * Call) -{ - struct UserInfo * ptr = NULL; - int i; - - for (i=1; i <= NumberofUsers; i++) - { - ptr = UserRecPtr[i]; - - if (_stricmp(ptr->Call, Call) == 0) return ptr; - - } - - return NULL; -} - -int GetNetInt(char * Line) -{ - char temp[1024]; - char * ptr = strlop(Line, ','); - int n = atoi(Line); - if (ptr == NULL) - Line[0] = 0; - else - { - strcpy(temp, ptr); - strcpy(Line, temp); - } - return n; -} - -VOID GetUserDatabase() -{ - struct UserInfo UserRec; - - FILE * Handle; - size_t ReadLen; - struct UserInfo * user; - time_t UserLimit = time(NULL) - (UserLifetime * 86400); // Oldest user to keep - int i; - - // See if user config is in main config - - group = config_lookup (&cfg, "BBSUsers"); - - if (group) - { - // We have User config in the main config file. so use that - - int index = 0; - char * stats; - struct MsgStats * Stats; - char * ptr, * ptr2; - - config_setting_t * entry = config_setting_get_elem (group, index++); - - // Initialise a new File - - UserRecPtr = malloc(sizeof(void *)); - UserRecPtr[0] = malloc(sizeof (struct UserInfo)); - memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); - UserRecPtr[0]->Length = sizeof (struct UserInfo); - - NumberofUsers = 0; - - while (entry) - { - char call[16]; - - // entry->name is call, will have * in front if a call stating woth number - - if (entry->name[0] == '*') - strcpy(call, &entry->name[1]); - else - strcpy(call, entry->name); - - user = AllocateUserRecord(call); - - ptr = entry->value.sval; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->Name, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->Address, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->HomeBBS, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->QRA, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->pass, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->ZIP, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) strcpy(user->CMSPass, ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->lastmsg = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->flags = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->PageLen = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->BBSNumber = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->RMSSSIDBits = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->WebSeqNo = atoi(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) user->TimeLastConnected = atol(ptr); - ptr = ptr2; - - ptr2 = strlop(ptr, '^'); - if (ptr) Stats = &user->Total; - stats = ptr; - - if (Stats == NULL) - { - NumberofUsers--; - free(user); - entry = config_setting_get_elem (group, index++); - continue; - } - - Stats->ConnectsIn = GetNetInt(stats); - Stats->ConnectsOut = GetNetInt(stats); - Stats->MsgsReceived[0] = GetNetInt(stats); - Stats->MsgsReceived[1] = GetNetInt(stats); - Stats->MsgsReceived[2] = GetNetInt(stats); - Stats->MsgsReceived[3] = GetNetInt(stats); - Stats->MsgsSent[0] = GetNetInt(stats); - Stats->MsgsSent[1] = GetNetInt(stats); - Stats->MsgsSent[2] = GetNetInt(stats); - Stats->MsgsSent[3] = GetNetInt(stats); - Stats->MsgsRejectedIn[0] = GetNetInt(stats); - Stats->MsgsRejectedIn[1] = GetNetInt(stats); - Stats->MsgsRejectedIn[2] = GetNetInt(stats); - Stats->MsgsRejectedIn[3] = GetNetInt(stats); - Stats->MsgsRejectedOut[0] = GetNetInt(stats); - Stats->MsgsRejectedOut[1] = GetNetInt(stats); - Stats->MsgsRejectedOut[2] = GetNetInt(stats); - Stats->MsgsRejectedOut[3] = GetNetInt(stats); - Stats->BytesForwardedIn[0] = GetNetInt(stats); - Stats->BytesForwardedIn[1] = GetNetInt(stats); - Stats->BytesForwardedIn[2] = GetNetInt(stats); - Stats->BytesForwardedIn[3] = GetNetInt(stats); - Stats->BytesForwardedOut[0] = GetNetInt(stats); - Stats->BytesForwardedOut[1] = GetNetInt(stats); - Stats->BytesForwardedOut[2] = GetNetInt(stats); - Stats->BytesForwardedOut[3] = GetNetInt(stats); - - Stats = &user->Last; - stats = ptr2; - - if (Stats == NULL) - { - NumberofUsers--; - free(user); - entry = config_setting_get_elem (group, index++); - continue; - } - - Stats->ConnectsIn = GetNetInt(stats); - Stats->ConnectsOut = GetNetInt(stats); - Stats->MsgsReceived[0] = GetNetInt(stats); - Stats->MsgsReceived[1] = GetNetInt(stats); - Stats->MsgsReceived[2] = GetNetInt(stats); - Stats->MsgsReceived[3] = GetNetInt(stats); - Stats->MsgsSent[0] = GetNetInt(stats); - Stats->MsgsSent[1] = GetNetInt(stats); - Stats->MsgsSent[2] = GetNetInt(stats); - Stats->MsgsSent[3] = GetNetInt(stats); - Stats->MsgsRejectedIn[0] = GetNetInt(stats); - Stats->MsgsRejectedIn[1] = GetNetInt(stats); - Stats->MsgsRejectedIn[2] = GetNetInt(stats); - Stats->MsgsRejectedIn[3] = GetNetInt(stats); - Stats->MsgsRejectedOut[0] = GetNetInt(stats); - Stats->MsgsRejectedOut[1] = GetNetInt(stats); - Stats->MsgsRejectedOut[2] = GetNetInt(stats); - Stats->MsgsRejectedOut[3] = GetNetInt(stats); - Stats->BytesForwardedIn[0] = GetNetInt(stats); - Stats->BytesForwardedIn[1] = GetNetInt(stats); - Stats->BytesForwardedIn[2] = GetNetInt(stats); - Stats->BytesForwardedIn[3] = GetNetInt(stats); - Stats->BytesForwardedOut[0] = GetNetInt(stats); - Stats->BytesForwardedOut[1] = GetNetInt(stats); - Stats->BytesForwardedOut[2] = GetNetInt(stats); - Stats->BytesForwardedOut[3] = GetNetInt(stats); - - - if ((user->flags & F_BBS) == 0) // Not BBS - Check Age - { - if (UserLifetime && user->TimeLastConnected) // Dont delete manually added Users that havent yet connected - { - if (user->TimeLastConnected < UserLimit) - { - // Too Old - ignore - - NumberofUsers--; - free(user); - entry = config_setting_get_elem (group, index++); - continue; - } - } - } - user->Temp = zalloc(sizeof (struct TempUserInfo)); - - if (user->lastmsg < 0 || user->lastmsg > LatestMsg) - user->lastmsg = LatestMsg; - - - entry = config_setting_get_elem (group, index++); - } - } - else - { - Handle = fopen(UserDatabasePath, "rb"); - - if (Handle == NULL) - { - // Initialise a new File - - UserRecPtr=malloc(sizeof(void *)); - UserRecPtr[0]= malloc(sizeof (struct UserInfo)); - memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); - UserRecPtr[0]->Length = sizeof (struct UserInfo); - - NumberofUsers = 0; - - return; - } - - - // Get First Record - - ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); - - if (ReadLen == 0) - { - // Duff file - - memset(&UserRec, 0, sizeof (struct UserInfo)); - UserRec.Length = sizeof (struct UserInfo); - } - else - { - // See if format has changed - - if (UserRec.Length == 0) - { - // Old format without a Length field - - struct OldUserInfo * OldRec = (struct OldUserInfo *)&UserRec; - int Users = OldRec->ConnectsIn; // User Count in control record - char Backup1[MAX_PATH]; - - // Create a backup in case reversion is needed and Reposition to first User record - - fclose(Handle); - - strcpy(Backup1, UserDatabasePath); - strcat(Backup1, ".oldformat"); - - CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak - - Handle = fopen(UserDatabasePath, "rb"); - - ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); // Skip Control Record - - // Set up control record - - UserRecPtr=malloc(sizeof(void *)); - UserRecPtr[0]= malloc(sizeof (struct UserInfo)); - memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); - UserRecPtr[0]->Length = sizeof (UserRec); - - NumberofUsers = 0; - -OldNext: - - ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); - - if (ReadLen > 0) - { - if (OldRec->Call[0] < '0') - goto OldNext; // Blank record - - user = AllocateUserRecord(OldRec->Call); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - - // Copy info from Old record - - user->lastmsg = OldRec->lastmsg; - user->Total.ConnectsIn = OldRec->ConnectsIn; - user->TimeLastConnected = OldRec->TimeLastConnected; - user->flags = OldRec->flags; - user->PageLen = OldRec->PageLen; - user->BBSNumber = OldRec->BBSNumber; - memcpy(user->Name, OldRec->Name, 18); - memcpy(user->Address, OldRec->Address, 61); - user->Total.MsgsReceived[0] = OldRec->MsgsReceived; - user->Total.MsgsSent[0] = OldRec->MsgsSent; - user->Total.MsgsRejectedIn[0] = OldRec->MsgsRejectedIn; // Messages we reject - user->Total.MsgsRejectedOut[0] = OldRec->MsgsRejectedOut; // Messages Rejectd by other end - user->Total.BytesForwardedIn[0] = OldRec->BytesForwardedIn; - user->Total.BytesForwardedOut[0] = OldRec->BytesForwardedOut; - user->Total.ConnectsOut = OldRec->ConnectsOut; // Forwarding Connects Out - user->RMSSSIDBits = OldRec->RMSSSIDBits; // SSID's to poll in RMS - memcpy(user->HomeBBS, OldRec->HomeBBS, 41); - memcpy(user->QRA, OldRec->QRA, 7); - memcpy(user->pass, OldRec->pass, 13); - memcpy(user->ZIP, OldRec->ZIP, 9); - - // Read any forwarding info, even if not a BBS. - // This allows a BBS to be temporarily set as a - // normal user without loosing forwarding info - - SetupForwardingStruct(user); - - if (user->flags & F_BBS) - { - // Defined as BBS - allocate and initialise forwarding structure - - // Add to BBS Chain; - - user->BBSNext = BBSChain; - BBSChain = user; - - // Save Highest BBS Number - - if (user->BBSNumber > HighestBBSNumber) HighestBBSNumber = user->BBSNumber; - } - goto OldNext; - } - - SortBBSChain(); - fclose(Handle); - - return; - } - } - - // Set up control record - - UserRecPtr=malloc(sizeof(void *)); - UserRecPtr[0]= malloc(sizeof (struct UserInfo)); - memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); - UserRecPtr[0]->Length = sizeof (UserRec); - - NumberofUsers = 0; - -Next: - - ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); - - if (ReadLen > 0) - { - if (UserRec.Call[0] < '0') - goto Next; // Blank record - - if (UserRec.TimeLastConnected == 0) - UserRec.TimeLastConnected = UserRec.xTimeLastConnected; - - if ((UserRec.flags & F_BBS) == 0) // Not BBS - Check Age - if (UserLifetime) // if limit set - if (UserRec.TimeLastConnected) // Dont delete manually added Users that havent yet connected - if (UserRec.TimeLastConnected < UserLimit) - goto Next; // Too Old - ignore - - user = AllocateUserRecord(UserRec.Call); - memcpy(user, &UserRec, sizeof (UserRec)); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - - user->ForwardingInfo = NULL; // In case left behind on crash - user->BBSNext = NULL; - user->POP3Locked = FALSE; - - if (user->lastmsg < 0 || user->lastmsg > LatestMsg) - user->lastmsg = LatestMsg; - - goto Next; - } - fclose(Handle); - } - - // Setting up BBS struct has been moved until all user record - // have been read so we can fix corrupt BBSNUmber - - for (i=1; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - - // Read any forwarding info, even if not a BBS. - // This allows a BBS to be temporarily set as a - // normal user without loosing forwarding info - - SetupForwardingStruct(user); - - if (user->flags & F_BBS) - { - // Add to BBS Chain; - - if (user->BBSNumber == NBBBS) // Fix corrupt records - { - user->BBSNumber = FindFreeBBSNumber(); - if (user->BBSNumber == 0) - user->BBSNumber = NBBBS; // cant really do much else - } - - user->BBSNext = BBSChain; - BBSChain = user; - -// Logprintf(LOG_BBS, NULL, '?', "BBS %s BBSNumber %d", user->Call, user->BBSNumber); - - // Save Highest BBS Number - - if (user->BBSNumber > HighestBBSNumber) - HighestBBSNumber = user->BBSNumber; - } - } - - // Check for dulicate BBS numbers - - for (i=1; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - - if (user->flags & F_BBS) - { - if (user->BBSNumber == 0) - user->BBSNumber = FindFreeBBSNumber(); - - CheckBBSNumber(user->BBSNumber); - } - } - - SortBBSChain(); -} - -VOID CopyUserDatabase() -{ - return; // User config now in main config file -/* - char Backup1[MAX_PATH]; - char Backup2[MAX_PATH]; - - // Keep 4 Generations - - strcpy(Backup2, UserDatabasePath); - strcat(Backup2, ".bak.3"); - - strcpy(Backup1, UserDatabasePath); - strcat(Backup1, ".bak.2"); - - DeleteFile(Backup2); // Remove old .bak.3 - MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 - - strcpy(Backup2, UserDatabasePath); - strcat(Backup2, ".bak.1"); - - MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 - - strcpy(Backup1, UserDatabasePath); - strcat(Backup1, ".bak"); - - MoveFile(Backup1, Backup2); //Move .bak to .bak.1 - - CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak -*/ -} - -VOID CopyConfigFile(char * ConfigName) -{ - char Backup1[MAX_PATH]; - char Backup2[MAX_PATH]; - - // Keep 4 Generations - - strcpy(Backup2, ConfigName); - strcat(Backup2, ".bak.3"); - - strcpy(Backup1, ConfigName); - strcat(Backup1, ".bak.2"); - - DeleteFile(Backup2); // Remove old .bak.3 - MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 - - strcpy(Backup2, ConfigName); - strcat(Backup2, ".bak.1"); - - MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 - - strcpy(Backup1, ConfigName); - strcat(Backup1, ".bak"); - - MoveFile(Backup1, Backup2); // Move .bak to .bak.1 - - CopyFile(ConfigName, Backup1, FALSE); // Copy to .bak -} - - - -VOID SaveUserDatabase() -{ - SaveConfig(ConfigName); // User config is now in main config file - GetConfig(ConfigName); - -/* - FILE * Handle; - size_t WriteLen; - int i; - - Handle = fopen(UserDatabasePath, "wb"); - - UserRecPtr[0]->Total.ConnectsIn = NumberofUsers; - - for (i=0; i <= NumberofUsers; i++) - { - WriteLen = fwrite(UserRecPtr[i], 1, (int)sizeof (struct UserInfo), Handle); - } - - fclose(Handle); -*/ - return; -} - -VOID GetMessageDatabase() -{ - struct MsgInfo MsgRec; - FILE * Handle; - size_t ReadLen; - struct MsgInfo * Msg; - char * MsgBytes; - int FileRecsize = sizeof(struct MsgInfo); // May be changed if reformating - BOOL Reformatting = FALSE; - char HEX[3] = ""; - int n; - - // See if Message Database is in main config - - group = config_lookup (&cfg, "MSGS"); - -// group = 0; - - if (group) - { - // We have User config in the main config file. so use that - - int index = 0; - char * ptr, * ptr2; - config_setting_t * entry = config_setting_get_elem (group, index++); - - // Initialise a new File - - MsgHddrPtr=malloc(sizeof(void *)); - MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); - NumberofMessages = 0; - MsgHddrPtr[0]->status = 2; - - if (entry) - { - // First Record has current message number - - ptr = entry->value.sval; - ptr2 = strlop(ptr, '|'); - ptr2 = strlop(ptr2, '|'); - if (ptr2) - LatestMsg = atoi(ptr2); - } - - entry = config_setting_get_elem (group, index++); - - while (entry) - { - // entry->name is MsgNo with 'R' in front - - ptr = entry->value.sval; - ptr2 = strlop(ptr, '|'); - - memset(&MsgRec, 0, sizeof(struct MsgInfo)); - - MsgRec.number = atoi(&entry->name[1]); - MsgRec.type = ptr[0]; - - ptr = ptr2; - - if (ptr == NULL) - { - entry = config_setting_get_elem (group, index++); - continue; - } - - ptr2 = strlop(ptr, '|'); - MsgRec.status = ptr[0]; - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.length = atoi(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.datereceived = atol(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.bbsfrom, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.via, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.from, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.to, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.bid, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.B2Flags = atoi(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.datecreated = atol(ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.datechanged = atol(ptr); - - ptr = ptr2; - if (ptr) ptr2 = strlop(ptr, '|'); - - if (ptr == NULL) - { - entry = config_setting_get_elem (group, index++); - continue; - } - - if (ptr[0]) - { - char String[50] = "00000000000000000000"; - String[20] = 0; - memcpy(String, ptr, strlen(ptr)); - for (n = 0; n < NBMASK; n++) - { - memcpy(HEX, &String[n * 2], 2); - MsgRec.fbbs[n] = (UCHAR)strtol(HEX, 0, 16); - } - } - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - - if (ptr == NULL) - { - entry = config_setting_get_elem (group, index++); - continue; - } - - if (ptr[0]) - { - char String[50] = "00000000000000000000"; - String[20] = 0; - memcpy(String, ptr, strlen(ptr)); - for (n = 0; n < NBMASK; n++) - { - memcpy(HEX, &String[n * 2], 2); - MsgRec.forw[n] = (UCHAR)strtol(HEX, 0, 16); - } - } - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) strcpy(MsgRec.emailfrom, ptr); - - ptr = ptr2; - ptr2 = strlop(ptr, '|'); - if (ptr) MsgRec.UTF8 = atoi(ptr); - - ptr = ptr2; - - if (ptr) - { - strcpy(MsgRec.title, ptr); - - MsgBytes = ReadMessageFileEx(&MsgRec); - - if (MsgBytes) - { - free(MsgBytes); - Msg = AllocateMsgRecord(); - memcpy(Msg, &MsgRec, sizeof (MsgRec)); - - MsgnotoMsg[Msg->number] = Msg; - - // Fix Corrupted NTS Messages - - if (Msg->type == 'N') - Msg->type = 'T'; - - // Look for corrupt FROM address (ending in @) - - strlop(Msg->from, '@'); - - BuildNNTPList(Msg); // Build NNTP Groups list - - // If any forward bits are set, increment count on corresponding BBS record. - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - { - if (FirstMessageIndextoForward == 0) - FirstMessageIndextoForward = NumberofMessages; // limit search - } - } - } - entry = config_setting_get_elem (group, index++); - } - - if (FirstMessageIndextoForward == 0) - FirstMessageIndextoForward = NumberofMessages; // limit search - - return; - } - - Handle = fopen(MsgDatabasePath, "rb"); - - if (Handle == NULL) - { - // Initialise a new File - - MsgHddrPtr=malloc(sizeof(void *)); - MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); - NumberofMessages = 0; - MsgHddrPtr[0]->status = 2; - - return; - } - - // Get First Record - - ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); - - if (ReadLen == 0) - { - // Duff file - - memset(&MsgRec, 0, sizeof (MsgRec)); - MsgRec.status = 2; - } - - // Set up control record - - MsgHddrPtr=malloc(sizeof(void *)); - MsgHddrPtr[0]= malloc(sizeof (MsgRec)); - memcpy(MsgHddrPtr[0], &MsgRec, sizeof (MsgRec)); - - LatestMsg=MsgHddrPtr[0]->length; - - NumberofMessages = 0; - - if (MsgRec.status == 1) // Used as file format version - // 0 = original, 1 = Extra email from addr, 2 = More BBS's. - { - char Backup1[MAX_PATH]; - - // Create a backup in case reversion is needed and Reposition to first User record - - fclose(Handle); - - strcpy(Backup1, MsgDatabasePath); - strcat(Backup1, ".oldformat"); - - CopyFile(MsgDatabasePath, Backup1, FALSE); // Copy to .oldformat - - Handle = fopen(MsgDatabasePath, "rb"); - - FileRecsize = sizeof(struct OldMsgInfo); - - ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); - - MsgHddrPtr[0]->status = 2; - } - -Next: - - ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); - - if (ReadLen > 0) - { - // Validate Header - - if (FileRecsize == sizeof(struct MsgInfo)) - { - if (MsgRec.type == 0 || MsgRec.number == 0) - goto Next; - - MsgBytes = ReadMessageFileEx(&MsgRec); - - if (MsgBytes) - { - // MsgRec.length = strlen(MsgBytes); - free(MsgBytes); - } - else - goto Next; - - Msg = AllocateMsgRecord(); - - memcpy(Msg, &MsgRec, +sizeof (MsgRec)); - } - else - { - // Resizing - record from file is an OldRecInfo - - struct OldMsgInfo * OldMessage = (struct OldMsgInfo *) &MsgRec; - - if (OldMessage->type == 0) - goto Next; - - if (OldMessage->number > 99999 || OldMessage->number < 1) - goto Next; - - Msg = AllocateMsgRecord(); - - - Msg->B2Flags = OldMessage->B2Flags; - memcpy(Msg->bbsfrom, OldMessage->bbsfrom, 7); - memcpy(Msg->bid, OldMessage->bid, 13); - Msg->datechanged = OldMessage->datechanged; - Msg->datecreated = OldMessage->datecreated; - Msg->datereceived = OldMessage->datereceived; - memcpy(Msg->emailfrom, OldMessage->emailfrom, 41); - memcpy(Msg->fbbs , OldMessage->fbbs, 10); - memcpy(Msg->forw , OldMessage->forw, 10); - memcpy(Msg->from, OldMessage->from, 7); - Msg->length = OldMessage->length; - Msg->nntpnum = OldMessage->nntpnum; - Msg->number = OldMessage->number; - Msg->status = OldMessage->status; - memcpy(Msg->title, OldMessage->title, 61); - memcpy(Msg->to, OldMessage->to, 7); - Msg->type = OldMessage->type; - memcpy(Msg->via, OldMessage->via, 41); - } - - MsgnotoMsg[Msg->number] = Msg; - - // Fix Corrupted NTS Messages - - if (Msg->type == 'N') - Msg->type = 'T'; - - // Look for corrupt FROM address (ending in @) - - strlop(Msg->from, '@'); - - // Move Dates if first run with new format - - if (Msg->datecreated == 0) - Msg->datecreated = Msg->xdatecreated; - - if (Msg->datereceived == 0) - Msg->datereceived = Msg->xdatereceived; - - if (Msg->datechanged == 0) - Msg->datechanged = Msg->xdatechanged; - - BuildNNTPList(Msg); // Build NNTP Groups list - - Msg->Locked = 0; // In case left locked - Msg->Defered = 0; // In case left set. - - // If any forward bits are set, increment count on corresponding BBS record. - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - { - if (FirstMessageIndextoForward == 0) - FirstMessageIndextoForward = NumberofMessages; // limit search - } - - goto Next; - } - - if (FirstMessageIndextoForward == 0) - FirstMessageIndextoForward = NumberofMessages; // limit search - - fclose(Handle); -} - -VOID CopyMessageDatabase() -{ - char Backup1[MAX_PATH]; - char Backup2[MAX_PATH]; - -// return; - - // Keep 4 Generations - - strcpy(Backup2, MsgDatabasePath); - strcat(Backup2, ".bak.3"); - - strcpy(Backup1, MsgDatabasePath); - strcat(Backup1, ".bak.2"); - - DeleteFile(Backup2); // Remove old .bak.3 - MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 - - strcpy(Backup2, MsgDatabasePath); - strcat(Backup2, ".bak.1"); - - MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 - - strcpy(Backup1, MsgDatabasePath); - strcat(Backup1, ".bak"); - - MoveFile(Backup1, Backup2); //Move .bak to .bak.1 - - strcpy(Backup2, MsgDatabasePath); - strcat(Backup2, ".bak"); - - CopyFile(MsgDatabasePath, Backup2, FALSE); // Copy to .bak - -} - -VOID SaveMessageDatabase() -{ - FILE * Handle; - size_t WriteLen; - int i; - char Key[16]; - struct MsgInfo *Msg; -// char CfgName[MAX_PATH]; - char HEXString1[64]; - char HEXString2[64]; - int n; -// char * CfgBuffer; - char Cfg[1024]; -// int CfgLen = 0; -// FILE * hFile; - -// SaveConfig(ConfigName); // Message Headers now in main config -// return; - -#ifdef LINBPQ - RefreshWebMailIndex(); -#else - if (pRefreshWebMailIndex) - pRefreshWebMailIndex(); -#endif - - Handle = fopen(MsgDatabasePath, "wb"); - - if (Handle == NULL) - { - CriticalErrorHandler("Failed to open message database"); - return; - } - - MsgHddrPtr[0]->status = 2; - MsgHddrPtr[0]->number = NumberofMessages; - MsgHddrPtr[0]->length = LatestMsg; - - for (i=0; i <= NumberofMessages; i++) - { - WriteLen = fwrite(MsgHddrPtr[i], 1, sizeof (struct MsgInfo), Handle); - - if (WriteLen != sizeof(struct MsgInfo)) - { - CriticalErrorHandler("Failed to write message database record"); - return; - } - } - - if (fclose(Handle) != 0) - CriticalErrorHandler("Failed to close message database"); - - for (i = 1; i <= NumberofMessages; i++) - { - Msg = MsgHddrPtr[i]; - - for (n = 0; n < NBMASK; n++) - sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); - - n = 39; - while (n >=0 && HEXString1[n] == '0') - HEXString1[n--] = 0; - - for (n = 0; n < NBMASK; n++) - sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); - - n = 39; - while (n >= 0 && HEXString2[n] == '0') - HEXString2[n--] = 0; - - sprintf(Key, "R%d:\r\n", i); - - n = sprintf(Cfg, "%c|%c|%d|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, - Msg->number, Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], - &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, - &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); - } - - return; -} - -VOID GetBIDDatabase() -{ - BIDRec BIDRec; - FILE * Handle; - size_t ReadLen; - BIDRecP BID; - int index = 0; - char * ptr, * ptr2; - - // If BID info is in main config file, use it - - group = config_lookup (&cfg, "BIDS"); - - if (group) - { - config_setting_t * entry = config_setting_get_elem (group, index++); - - BIDRecPtr=malloc(sizeof(void *)); - BIDRecPtr[0]= malloc(sizeof (BIDRec)); - memset(BIDRecPtr[0], 0, sizeof (BIDRec)); - NumberofBIDs = 0; - - while (entry) - { - // entry->name is Bid with 'R' in front - - ptr = entry->value.sval; - ptr2 = strlop(ptr, '|'); - - if (ptr && ptr2) - { - BID = AllocateBIDRecord(); - strcpy(BID->BID, &entry->name[1]); - BID->mode = atoi(ptr); - BID->u.timestamp = atoi(ptr2); - - if (BID->u.timestamp == 0) - BID->u.timestamp = LOWORD(time(NULL)/86400); - - } - entry = config_setting_get_elem (group, index++); - } - return; - } - - Handle = fopen(BIDDatabasePath, "rb"); - - if (Handle == NULL) - { - // Initialise a new File - - BIDRecPtr=malloc(sizeof(void *)); - BIDRecPtr[0]= malloc(sizeof (BIDRec)); - memset(BIDRecPtr[0], 0, sizeof (BIDRec)); - NumberofBIDs = 0; - - return; - } - - - // Get First Record - - ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); - - if (ReadLen == 0) - { - // Duff file - - memset(&BIDRec, 0, sizeof (BIDRec)); - } - - // Set up control record - - BIDRecPtr = malloc(sizeof(void *)); - BIDRecPtr[0] = malloc(sizeof (BIDRec)); - memcpy(BIDRecPtr[0], &BIDRec, sizeof (BIDRec)); - - NumberofBIDs = 0; - -Next: - - ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); - - if (ReadLen > 0) - { - BID = AllocateBIDRecord(); - memcpy(BID, &BIDRec, sizeof (BIDRec)); - - if (BID->u.timestamp == 0) - BID->u.timestamp = LOWORD(time(NULL)/86400); - - goto Next; - } - - fclose(Handle); -} - -VOID CopyBIDDatabase() -{ - char Backup[MAX_PATH]; - -// return; - - - strcpy(Backup, BIDDatabasePath); - strcat(Backup, ".bak"); - - CopyFile(BIDDatabasePath, Backup, FALSE); -} - -VOID SaveBIDDatabase() -{ - FILE * Handle; - size_t WriteLen; - int i; - -// return; // Bids are now in main config and are saved when message is saved - - Handle = fopen(BIDDatabasePath, "wb"); - - BIDRecPtr[0]->u.msgno = NumberofBIDs; // First Record has file size - - for (i=0; i <= NumberofBIDs; i++) - { - WriteLen = fwrite(BIDRecPtr[i], 1, sizeof (BIDRec), Handle); - } - - fclose(Handle); - - return; -} - -BIDRec * LookupBID(char * BID) -{ - BIDRec * ptr = NULL; - int i; - - for (i=1; i <= NumberofBIDs; i++) - { - ptr = BIDRecPtr[i]; - - if (_stricmp(ptr->BID, BID) == 0) - return ptr; - } - - return NULL; -} - -BIDRec * LookupTempBID(char * BID) -{ - BIDRec * ptr = NULL; - int i; - - for (i=1; i <= NumberofTempBIDs; i++) - { - ptr = TempBIDRecPtr[i]; - - if (_stricmp(ptr->BID, BID) == 0) return ptr; - } - - return NULL; -} - -VOID RemoveTempBIDS(CIRCUIT * conn) -{ - // Remove any Temp BID records for conn. Called when connection closes - Msgs will be complete or failed - - if (NumberofTempBIDs == 0) - return; - else - { - BIDRec * ptr = NULL; - BIDRec ** NewTempBIDRecPtr = zalloc((NumberofTempBIDs+1) * sizeof(void *)); - int i = 0, n; - - GetSemaphore(&AllocSemaphore, 0); - - for (n = 1; n <= NumberofTempBIDs; n++) - { - ptr = TempBIDRecPtr[n]; - - if (ptr) - { - if (ptr->u.conn == conn) - // Remove this entry - free(ptr); - else - NewTempBIDRecPtr[++i] = ptr; - } - } - - NumberofTempBIDs = i; - - free(TempBIDRecPtr); - - TempBIDRecPtr = NewTempBIDRecPtr; - FreeSemaphore(&AllocSemaphore); - } - -} - -VOID GetBadWordFile() -{ - FILE * Handle; - DWORD FileSize; - char * ptr1, * ptr2; - struct stat STAT; - - if (stat(BadWordsPath, &STAT) == -1) - return; - - FileSize = STAT.st_size; - - Handle = fopen(BadWordsPath, "rb"); - - if (Handle == NULL) - return; - - // Release old info in case a re-read - - if (BadWords) free(BadWords); - if (BadFile) free(BadFile); - - BadWords = NULL; - BadFile = NULL; - NumberofBadWords = 0; - - BadFile = malloc(FileSize+1); - - fread(BadFile, 1, FileSize, Handle); - - fclose(Handle); - - BadFile[FileSize]=0; - - _strlwr(BadFile); // Compares are case-insensitive - - ptr1 = BadFile; - - while (ptr1) - { - if (*ptr1 == '\n') ptr1++; - - ptr2 = strtok_s(NULL, "\r\n", &ptr1); - if (ptr2) - { - if (*ptr2 != '#') - { - BadWords = realloc(BadWords,(++NumberofBadWords+1) * sizeof(void *)); - BadWords[NumberofBadWords] = ptr2; - } - } - else - break; - } -} - -BOOL CheckBadWord(char * Word, char * Msg) -{ - char * ptr1 = Msg, * ptr2; - size_t len = strlen(Word); - - while (*ptr1) // Stop at end - { - ptr2 = strstr(ptr1, Word); - - if (ptr2 == NULL) - return FALSE; // OK - - // Only bad if it ia not part of a longer word - - if ((ptr2 == Msg) || !(isalpha(*(ptr2 - 1)))) // No alpha before - if (!(isalpha(*(ptr2 + len)))) // No alpha after - return TRUE; // Bad word - - // Keep searching - - ptr1 = ptr2 + len; - } - - return FALSE; // OK -} - -BOOL CheckBadWords(char * Msg) -{ - char * dupMsg = _strlwr(_strdup(Msg)); - int i; - - for (i = 1; i <= NumberofBadWords; i++) - { - if (CheckBadWord(BadWords[i], dupMsg)) - { - free(dupMsg); - return TRUE; // Bad - } - } - - free(dupMsg); - return FALSE; // OK - -} - -VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user) -{ - if (user->flags & F_Expert) - ExpandAndSendMessage(conn, ExpertWelcomeMsg, LOG_BBS); - else if (conn->NewUser) - ExpandAndSendMessage(conn, NewWelcomeMsg, LOG_BBS); - else - ExpandAndSendMessage(conn, WelcomeMsg, LOG_BBS); - - if (user->HomeBBS[0] == 0 && !DontNeedHomeBBS) - BBSputs(conn, "Please enter your Home BBS using the Home command.\rYou may also enter your QTH and ZIP/Postcode using qth and zip commands.\r"); - -// if (user->flags & F_Temp_B2_BBS) -// nodeprintf(conn, "%s CMS >\r", BBSName); -// else - SendPrompt(conn, user); -} - -VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user) -{ - if (user->Temp->ListSuspended) - return; // Dont send prompt if pausing a listing - - if (user->flags & F_Expert) - ExpandAndSendMessage(conn, ExpertPrompt, LOG_BBS); - else if (conn->NewUser) - ExpandAndSendMessage(conn, NewPrompt, LOG_BBS); - else - ExpandAndSendMessage(conn, Prompt, LOG_BBS); - -// if (user->flags & F_Expert) -// nodeprintf(conn, "%s\r", ExpertPrompt); -// else if (conn->NewUser) -// nodeprintf(conn, "%s\r", NewPrompt); -// else -// nodeprintf(conn, "%s\r", Prompt); -} - - - -VOID * _zalloc(size_t len) -{ - // ?? malloc and clear - - void * ptr; - - ptr=malloc(len); - memset(ptr, 0, len); - - return ptr; -} - -BOOL isAMPRMsg(char * Addr) -{ - // See if message is addressed to ampr.org and is either - // for us or we have SendAMPRDirect (ie don't need RMS or SMTP to send it) - - size_t toLen = strlen(Addr); - - if (_memicmp(&Addr[toLen - 8], "ampr.org", 8) == 0) - { - // message is for ampr.org - - char toCall[48]; - char * via; - - strcpy(toCall, _strupr(Addr)); - - via = strlop(toCall, '@'); - - if (_stricmp(via, AMPRDomain) == 0) - { - // message is for us. - - return TRUE; - } - - if (SendAMPRDirect) - { - // We want to send ampr mail direct to host. Queue to BBS AMPR - - if (FindAMPR()) - { - // We have bbs AMPR - - return TRUE; - } - } - } - return FALSE; -} - -struct UserInfo * FindAMPR() -{ - struct UserInfo * bbs; - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - if (strcmp(bbs->Call, "AMPR") == 0) - return bbs; - } - - return NULL; -} - -struct UserInfo * FindRMS() -{ - struct UserInfo * bbs; - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - if (strcmp(bbs->Call, "RMS") == 0) - return bbs; - } - - return NULL; -} - -struct UserInfo * FindBBS(char * Name) -{ - struct UserInfo * bbs; - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - if (strcmp(bbs->Call, Name) == 0) - return bbs; - } - - return NULL; -} - -int CountConnectionsOnPort(int CheckPort) -{ - int n, Count = 0; - CIRCUIT * conn; - int port, sesstype, paclen, maxframe, l4window; - char callsign[11]; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (conn->Active) - { - GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); - if (port == CheckPort) - Count++; - } - } - - return Count; -} - - -BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type) -{ - char ** Calls; - - if (Type == 'B' && FilterWPBulls && _stricmp(To, "WP") == 0) - return TRUE; - - if (RejFrom && From) - { - Calls = RejFrom; - - while(Calls[0]) - { - if (_stricmp(Calls[0], From) == 0) - return TRUE; - - Calls++; - } - } - - if (RejTo && To) - { - Calls = RejTo; - - while(Calls[0]) - { - if (_stricmp(Calls[0], To) == 0) - return TRUE; - - Calls++; - } - } - - if (RejAt && ATBBS) - { - Calls = RejAt; - - while(Calls[0]) - { - if (_stricmp(Calls[0], ATBBS) == 0) - return TRUE; - - Calls++; - } - } - - if (RejBID && BID) - { - Calls = RejBID; - - while(Calls[0]) - { - if (Calls[0][0] == '*') - { - if (stristr(BID, &Calls[0][1])) - return TRUE; - } - else - { - if (_stricmp(BID, Calls[0]) == 0) - return TRUE; - } - - Calls++; - } - } - return FALSE; // Ok to accept -} - -BOOL CheckValidCall(char * From) -{ - unsigned int i; - - if (DontCheckFromCall) - return TRUE; - - if (strcmp(From, "SYSOP") == 0 || strcmp(From, "SYSTEM") == 0 || - strcmp(From, "IMPORT") == 0 || strcmp(From, "SMTP:") == 0 || strcmp(From, "RMS:") == 0) - return TRUE; - - for (i = 1; i < strlen(From); i++) // skip first which may also be digit - { - if (isdigit(From[i])) - { - // Has a digit. Check Last is not digit - - if (isalpha(From[strlen(From) - 1])) - return TRUE; - } - } - - // No digit, return false - - return FALSE; -} - -BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID) -{ - char ** Calls; - - if (HoldFrom && From) - { - Calls = HoldFrom; - - while(Calls[0]) - { - if (_stricmp(Calls[0], From) == 0) - return TRUE; - - Calls++; - } - } - - if (HoldTo && To) - { - Calls = HoldTo; - - while(Calls[0]) - { - if (_stricmp(Calls[0], To) == 0) - return TRUE; - - Calls++; - } - } - - if (HoldAt && ATBBS) - { - Calls = HoldAt; - - while(Calls[0]) - { - if (_stricmp(Calls[0], ATBBS) == 0) - return TRUE; - - Calls++; - } - } - - if (HoldBID && BID) - { - Calls = HoldBID; - - while(Calls[0]) - { - if (Calls[0][0] == '*') - { - if (stristr(BID, &Calls[0][1])) - return TRUE; - } - else - { - if (_stricmp(BID, Calls[0]) == 0) - return TRUE; - } - - Calls++; - } - } - return FALSE; // Ok to accept -} - -BOOL CheckifLocalRMSUser(char * FullTo) -{ - struct UserInfo * user = LookupCall(FullTo); - - if (user) - if (user->flags & F_POLLRMS) - return TRUE; - - return FALSE; - -} - - - -int check_fwd_bit(char *mask, int bbsnumber) -{ - if (bbsnumber) - return (mask[(bbsnumber - 1) / 8] & (1 << ((bbsnumber - 1) % 8))); - else - return 0; -} - - -void set_fwd_bit(char *mask, int bbsnumber) -{ - if (bbsnumber) - mask[(bbsnumber - 1) / 8] |= (1 << ((bbsnumber - 1) % 8)); -} - - -void clear_fwd_bit (char *mask, int bbsnumber) -{ - if (bbsnumber) - mask[(bbsnumber - 1) / 8] &= (~(1 << ((bbsnumber - 1) % 8))); -} - -VOID BBSputs(CIRCUIT * conn, char * buf) -{ - // Sends to user and logs - - WriteLogLine(conn, '>',buf, (int)strlen(buf) -1, LOG_BBS); - - QueueMsg(conn, buf, (int)strlen(buf)); -} - -VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...) -{ - char Mess[1000]; - int len; - va_list(arglist); - - - va_start(arglist, format); - len = vsprintf(Mess, format, arglist); - - QueueMsg(conn, Mess, len); - - WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); - - return; -} - -// nodeprintfEx add a LF if NEEFLF is set - -VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...) -{ - char Mess[1000]; - int len; - va_list(arglist); - - - va_start(arglist, format); - len = vsprintf(Mess, format, arglist); - - QueueMsg(conn, Mess, len); - - WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); - - if (conn->BBSFlags & NEEDLF) - QueueMsg(conn, "\r", 1); - - return; -} - - -int compare( const void *arg1, const void *arg2 ); - -VOID SortBBSChain() -{ - struct UserInfo * user; - struct UserInfo * users[161]; - int i = 0, n; - - // Get array of addresses - - for (user = BBSChain; user; user = user->BBSNext) - { - users[i++] = user; - if (i > 160) break; - } - - qsort((void *)users, i, sizeof(void *), compare ); - - BBSChain = NULL; - - // Rechain (backwards, as entries ate put on front of chain) - - for (n = i-1; n >= 0; n--) - { - users[n]->BBSNext = BBSChain; - BBSChain = users[n]; - } -} - -int compare(const void *arg1, const void *arg2) -{ - // Compare Calls. Fortunately call is at start of stuct - - return _stricmp(*(char**)arg1 , *(char**)arg2); -} - -int CountMessagesTo(struct UserInfo * user, int * Unread) -{ - int i, Msgs = 0; - UCHAR * Call = user->Call; - - *Unread = 0; - - for (i = NumberofMessages; i > 0; i--) - { - if (MsgHddrPtr[i]->status == 'K') - continue; - - if (_stricmp(MsgHddrPtr[i]->to, Call) == 0) - { - Msgs++; - if (MsgHddrPtr[i]->status == 'N') - *Unread = *Unread + 1; - } - } - return(Msgs); -} - - - -// Custimised message handling routines. -/* - Variables - a subset of those used by FBB - - $C : Number of the next message. - $I : First name of the connected user. - $L : Number of the latest message. - $N : Number of active messages - $U : Callsign of the connected user. - $W : Inserts a carriage return. - $Z : Last message read by the user (L command). - %X : Number of messages for the user. - %x : Number of new messages for the user. -*/ - -VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG) -{ - char NewMessage[10000]; - char * OldP = Msg; - char * NewP = NewMessage; - char * ptr, * pptr; - size_t len; - char Dollar[] = "$"; - char CR[] = "\r"; - char num[20]; - int Msgs = 0, Unread = 0; - - ptr = strchr(OldP, '$'); - - while (ptr) - { - len = ptr - OldP; // Chars before $ - memcpy(NewP, OldP, len); - NewP += len; - - switch (*++ptr) - { - case 'I': // First name of the connected user. - - pptr = conn->UserPointer->Name; - break; - - case 'L': // Number of the latest message. - - sprintf(num, "%d", LatestMsg); - pptr = num; - break; - - case 'N': // Number of active messages. - - sprintf(num, "%d", NumberofMessages); - pptr = num; - break; - - case 'U': // Callsign of the connected user. - - pptr = conn->UserPointer->Call; - break; - - case 'W': // Inserts a carriage return. - - pptr = CR; - break; - - case 'Z': // Last message read by the user (L command). - - sprintf(num, "%d", conn->UserPointer->lastmsg); - pptr = num; - break; - - case 'X': // Number of messages for the user. - - Msgs = CountMessagesTo(conn->UserPointer, &Unread); - sprintf(num, "%d", Msgs); - pptr = num; - break; - - case 'x': // Number of new messages for the user. - - Msgs = CountMessagesTo(conn->UserPointer, &Unread); - sprintf(num, "%d", Unread); - pptr = num; - break; - - case 'F': // Number of new messages to forward to this BBS. - - Msgs = CountMessagestoForward(conn->UserPointer); - sprintf(num, "%d", Msgs); - pptr = num; - break; - - default: - - pptr = Dollar; // Just Copy $ - } - - len = strlen(pptr); - memcpy(NewP, pptr, len); - NewP += len; - - OldP = ++ptr; - ptr = strchr(OldP, '$'); - } - - strcpy(NewP, OldP); - - len = RemoveLF(NewMessage, (int)strlen(NewMessage)); - - WriteLogLine(conn, '>', NewMessage, (int)len, LOG); - QueueMsg(conn, NewMessage, (int)len); -} - -BOOL isdigits(char * string) -{ - // Returns TRUE id sting is decimal digits - - size_t i, n = strlen(string); - - for (i = 0; i < n; i++) - { - if (isdigit(string[i]) == FALSE) return FALSE; - } - return TRUE; -} - -BOOL wildcardcompare(char * Target, char * Match) -{ - // Do a compare with string *string string* *string* - - // Strings should all be UC - - char Pattern[100]; - char * firststar; - - strcpy(Pattern, Match); - firststar = strchr(Pattern,'*'); - - if (firststar) - { - size_t Len = strlen(Pattern); - - if (Pattern[0] == '*' && Pattern[Len - 1] == '*') // * at start and end - { - Pattern[Len - 1] = 0; - return !(strstr(Target, &Pattern[1]) == NULL); - } - if (Pattern[0] == '*') // * at start - { - // Compare the last len - 1 chars of Target - - size_t Targlen = strlen(Target); - size_t Comparelen = Targlen - (Len - 1); - - if (Len == 1) // Just * - return TRUE; - - if (Comparelen < 0) // Too Short - return FALSE; - - return (memcmp(&Target[Comparelen], &Pattern[1], Len - 1) == 0); - } - - // Must be * at end - compare first Len-1 char - - return (memcmp(Target, Pattern, Len - 1) == 0); - } - - // No WildCards - straight strcmp - return (strcmp(Target, Pattern) == 0); -} - -#ifndef LINBPQ - -PrintMessage(HDC hDC, struct MsgInfo * Msg); - -PrintMessages(HWND hDlg, int Count, int * Indexes) -{ - int i, CurrentMsgIndex; - char MsgnoText[10]; - int Msgno; - struct MsgInfo * Msg; - int Len = MAX_PATH; - BOOL hResult; - PRINTDLG pdx = {0}; - HDC hDC; - -// CHOOSEFONT cf; - LOGFONT lf; - HFONT hFont; - - - // Initialize the PRINTDLG structure. - - pdx.lStructSize = sizeof(PRINTDLG); - pdx.hwndOwner = hWnd; - pdx.hDevMode = NULL; - pdx.hDevNames = NULL; - pdx.hDC = NULL; - pdx.Flags = PD_RETURNDC | PD_COLLATE; - pdx.nMinPage = 1; - pdx.nMaxPage = 1000; - pdx.nCopies = 1; - pdx.hInstance = 0; - pdx.lpPrintTemplateName = NULL; - - // Invoke the Print property sheet. - - hResult = PrintDlg(&pdx); - - memset(&lf, 0, sizeof(LOGFONT)); - - /* - - // Initialize members of the CHOOSEFONT structure. - - cf.lStructSize = sizeof(CHOOSEFONT); - cf.hwndOwner = (HWND)NULL; - cf.hDC = pdx.hDC; - cf.lpLogFont = &lf; - cf.iPointSize = 0; - cf.Flags = CF_PRINTERFONTS | CF_FIXEDPITCHONLY; - cf.rgbColors = RGB(0,0,0); - cf.lCustData = 0L; - cf.lpfnHook = (LPCFHOOKPROC)NULL; - cf.lpTemplateName = (LPSTR)NULL; - cf.hInstance = (HINSTANCE) NULL; - cf.lpszStyle = (LPSTR)NULL; - cf.nFontType = PRINTER_FONTTYPE; - cf.nSizeMin = 0; - cf.nSizeMax = 0; - - // Display the CHOOSEFONT common-dialog box. - - ChooseFont(&cf); - - // Create a logical font based on the user's - // selection and return a handle identifying - // that font. -*/ - - lf.lfHeight = -56; - lf.lfWeight = 600; - lf.lfOutPrecision = 3; - lf.lfClipPrecision = 2; - lf.lfQuality = 1; - lf.lfPitchAndFamily = '1'; - strcpy (lf.lfFaceName, "Courier New"); - - hFont = CreateFontIndirect(&lf); - - if (hResult) - { - // User clicked the Print button, so use the DC and other information returned in the - // PRINTDLG structure to print the document. - - DOCINFO pdi; - - pdi.cbSize = sizeof(DOCINFO); - pdi.lpszDocName = "BBS Message Print"; - pdi.lpszOutput = NULL; - pdi.lpszDatatype = "RAW"; - pdi.fwType = 0; - - hDC = pdx.hDC; - - SelectObject(hDC, hFont); - - StartDoc(hDC, &pdi); - StartPage(hDC); - - for (i = 0; i < Count; i++) - { - SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Indexes[i], (LPARAM)(LPCTSTR)&MsgnoText); - - Msgno = atoi(MsgnoText); - - for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) - { - Msg = MsgHddrPtr[CurrentMsgIndex]; - - if (Msg->number == Msgno) - { - PrintMessage(hDC, Msg); - break; - } - } - } - - EndDoc(hDC); - } - - if (pdx.hDevMode != NULL) - GlobalFree(pdx.hDevMode); - if (pdx.hDevNames != NULL) - GlobalFree(pdx.hDevNames); - - if (pdx.hDC != NULL) - DeleteDC(pdx.hDC); - - return 0; -} - -PrintMessage(HDC hDC, struct MsgInfo * Msg) -{ - int Len = MAX_PATH; - char * MsgBytes; - char * Save; - int Msglen; - - StartPage(hDC); - - Save = MsgBytes = ReadMessageFile(Msg->number); - - Msglen = Msg->length; - - if (MsgBytes) - { - char Hddr[1000]; - char FullTo[100]; - int HRes, VRes; - char * ptr1, * ptr2; - int LineLen; - - RECT Rect; - - if (_stricmp(Msg->to, "RMS") == 0) - sprintf(FullTo, "RMS:%s", Msg->via); - else - if (Msg->to[0] == 0) - sprintf(FullTo, "smtp:%s", Msg->via); - else - strcpy(FullTo, Msg->to); - - - sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", - Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); - - - if (Msg->B2Flags & B2Msg) - { - // Remove B2 Headers (up to the File: Line) - - char * ptr; - ptr = strstr(MsgBytes, "Body:"); - if (ptr) - { - Msglen = atoi(ptr + 5); - ptr = strstr(ptr, "\r\n\r\n"); - } - if (ptr) - MsgBytes = ptr + 4; - } - - HRes = GetDeviceCaps(hDC, HORZRES) - 50; - VRes = GetDeviceCaps(hDC, VERTRES) - 50; - - Rect.top = 50; - Rect.left = 50; - Rect.right = HRes; - Rect.bottom = VRes; - - DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_CALCRECT | DT_WORDBREAK); - DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_WORDBREAK); - - // process message a line at a time. When page is full, output a page break - - ptr1 = MsgBytes; - ptr2 = ptr1; - - while (Msglen-- > 0) - { - if (*ptr1++ == '\r') - { - // Output this line - - // First check if it will fit - - Rect.top = Rect.bottom; - Rect.right = HRes; - Rect.bottom = VRes; - - LineLen = ptr1 - ptr2 - 1; - - if (LineLen == 0) // Blank line - Rect.bottom = Rect.top + 40; - else - DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); - - if (Rect.bottom >= VRes) - { - EndPage(hDC); - StartPage(hDC); - - Rect.top = 50; - Rect.bottom = VRes; - if (LineLen == 0) // Blank line - Rect.bottom = Rect.top + 40; - else - DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); - } - - if (LineLen == 0) // Blank line - Rect.bottom = Rect.top + 40; - else - DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_WORDBREAK); - - if (*(ptr1) == '\n') - { - ptr1++; - Msglen--; - } - - ptr2 = ptr1; - } - } - - free(Save); - - EndPage(hDC); - - } - return 0; -} - -#endif - - -int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup) -{ - char FileName[MAX_PATH] = "Messages.in"; - int Files = 0; - int WriteLen=0; - FILE *in; - CIRCUIT dummyconn; - struct UserInfo User; - int Index = 0; - - char Buffer[100000]; - char *buf = Buffer; - - if (FN[0]) // Name supplled - strcpy(FileName, FN); - - else - { -#ifndef LINBPQ - OPENFILENAME Ofn; - - memset(&Ofn, 0, sizeof(Ofn)); - - Ofn.lStructSize = sizeof(OPENFILENAME); - Ofn.hInstance = hInst; - Ofn.hwndOwner = MainWnd; - Ofn.lpstrFilter = NULL; - Ofn.lpstrFile= FileName; - Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); - Ofn.lpstrFileTitle = NULL; - Ofn.nMaxFileTitle = 0; - Ofn.lpstrInitialDir = BaseDir; - Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; - Ofn.lpstrTitle = NULL;//; - - if (!GetOpenFileName(&Ofn)) - return 0; -#endif - } - - in = fopen(FileName, "rb"); - - if (!(in)) - { - char msg[500]; - sprintf_s(msg, sizeof(msg), "Failed to open %s", FileName); - if (conn) - nodeprintf(conn, "%s\r", msg); -#ifdef WIN32 - else - if (Nopopup == FALSE) - MessageBox(NULL, msg, "BPQMailChat", MB_OK); -#endif - return 0; - } - - memset(&dummyconn, 0, sizeof(CIRCUIT)); - memset(&User, 0, sizeof(struct UserInfo)); - - if (conn == 0) - { - conn = &dummyconn; - - dummyconn.UserPointer = &User; // Was SYSOPCall, but I think that is wrong. - strcpy(User.Call, "IMPORT"); - User.flags |= F_EMAIL; - dummyconn.sysop = TRUE; - dummyconn.BBSFlags = BBS; - - strcpy(dummyconn.Callsign, "IMPORT"); - } - - while(fgets(Buffer, 99999, in)) - { - // First line should start SP/SB ?ST? - - char * From = NULL; - char * BID = NULL; - char * ATBBS = NULL; - char seps[] = " \t\r"; - struct MsgInfo * Msg; - char To[100]= ""; - int msglen; - char * Context; - char * Arg1, * Cmd; - -NextMessage: - - From = NULL; - BID = NULL; - ATBBS = NULL; - To[0]= 0; - - Sleep(100); - - strlop(Buffer, 10); - strlop(Buffer, 13); // Remove cr and/or lf - - if (Buffer[0] == 0) //Blank Line - continue; - - WriteLogLine(conn, '>', Buffer, (int)strlen(Buffer), LOG_BBS); - - if (dummyconn.sysop == 0) - { - nodeprintf(conn, "%s\r", Buffer); - Flush(conn); - } - - Cmd = strtok_s(Buffer, seps, &Context); - - if (Cmd == NULL) - { - fclose(in); - return Files; - } - - Arg1 = strtok_s(NULL, seps, &Context); - - if (Arg1 == NULL) - { - if (dummyconn.sysop) - Debugprintf("Bad Import Line %s", Buffer); - else - nodeprintf(conn, "Bad Import Line %s\r", Buffer); - - fclose(in); - return Files; - } - - strcpy(To, Arg1); - - if (DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) - { - if (CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL)) - { - Msg = conn->TempMsg; - - // SP is Ok, read message; - - ClearQueue(conn); - - fgets(Buffer, 99999, in); - strlop(Buffer, 10); - strlop(Buffer, 13); // Remove cr and/or lf - if (strlen(Buffer) > 60) - Buffer[60] = 0; - - strcpy(Msg->title, Buffer); - - // Read the lines - - conn->Flags |= GETTINGMESSAGE; - - Buffer[0] = 0; - - fgets(Buffer, 99999, in); - - while ((conn->Flags & GETTINGMESSAGE) && Buffer[0]) - { - strlop(Buffer, 10); - strlop(Buffer, 13); // Remove cr and/or lf - msglen = (int)strlen(Buffer); - Buffer[msglen++] = 13; - ProcessMsgLine(conn, conn->UserPointer,Buffer, msglen); - - Buffer[0] = 0; - fgets(Buffer, 99999, in); - } - - // Message completed (or off end of file) - - Files ++; - - ClearQueue(conn); - - if (Buffer[0]) - goto NextMessage; // We have read the SP/SB line; - else - { - fclose(in); - return Files; - } - } - else - { - // Create failed - - Flush(conn); - } - } - - // Search for next message - - Buffer[0] = 0; - fgets(Buffer, 99999, in); - - while (Buffer[0]) - { - strlop(Buffer, 10); - strlop(Buffer, 13); // Remove cr and/or lf - - if (_stricmp(Buffer, "/EX") == 0) - { - // Found end - - Buffer[0] = 0; - fgets(Buffer, 99999, in); - - if (dummyconn.sysop) - ClearQueue(conn); - else - Flush(conn); - - if (Buffer[0]) - goto NextMessage; // We have read the SP/SB line; - } - - Buffer[0] = 0; - fgets(Buffer, 99999, in); - } - } - - fclose(in); - - if (dummyconn.sysop) - ClearQueue(conn); - else - Flush(conn); - - return Files; -} -char * ReadMessageFileEx(struct MsgInfo * MsgRec) -{ - // Sets Message Size from File Size - - int msgno = MsgRec->number; - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char * MsgBytes; - struct stat STAT; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); - - if (stat(MsgFile, &STAT) == -1) - return NULL; - - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile == NULL) - return NULL; - - MsgBytes=malloc(FileSize+1); - - fread(MsgBytes, 1, FileSize, hFile); - - fclose(hFile); - - MsgBytes[FileSize]=0; - MsgRec->length = FileSize; - - return MsgBytes; -} - -char * ReadMessageFile(int msgno) -{ - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char * MsgBytes; - struct stat STAT; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); - - if (stat(MsgFile, &STAT) == -1) - return NULL; - - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile == NULL) - return NULL; - - MsgBytes = malloc(FileSize + 100); // A bit of space for alias substitution on B2 - - fread(MsgBytes, 1, FileSize, hFile); - - fclose(hFile); - - MsgBytes[FileSize]=0; - - return MsgBytes; -} - - -int QueueMsg(ConnectionInfo * conn, char * msg, int len) -{ - // Add Message to queue for this connection - - // UCHAR * OutputQueue; // Messages to user - // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message - // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. - - // Create or extend buffer - - GetSemaphore(&OutputSEM, 0); - - conn->OutputQueue=realloc(conn->OutputQueue, conn->OutputQueueLength + len); - - if (conn->OutputQueue == NULL) - { - // relloc failed - should never happen, but clean up - - CriticalErrorHandler("realloc failed to expand output queue"); - FreeSemaphore(&OutputSEM); - return 0; - } - - memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); - conn->OutputQueueLength += len; - FreeSemaphore(&OutputSEM); - - return len; -} - -void TrytoSend() -{ - // call Flush on any connected streams with queued data - - ConnectionInfo * conn; - struct ConsoleInfo * Cons; - - int n; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (conn->Active == TRUE) - { - Flush(conn); - - // if an FLARQ mail has been sent see if queues have cleared - - if (conn->BBSFlags & YAPPTX) - { - YAPPSendData(conn); - } - else if (conn->OutputQueue == NULL && (conn->BBSFlags & ARQMAILACK)) - { - int n = TXCount(conn->BPQStream); // All Sent and Acked? - - if (n == 0) - { - struct MsgInfo * Msg = conn->FwdMsg; - - conn->ARQClearCount--; - - if (conn->ARQClearCount <= 0) - { - Logprintf(LOG_BBS, conn, '>', "ARQ Send Complete"); - - // Mark mail as sent, and look for more - - clear_fwd_bit(Msg->fbbs, conn->UserPointer->BBSNumber); - set_fwd_bit(Msg->forw, conn->UserPointer->BBSNumber); - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) - { - Msg->status = 'F'; // Mark as forwarded - Msg->datechanged=time(NULL); - } - - conn->BBSFlags &= ~ARQMAILACK; - conn->UserPointer->ForwardingInfo->MsgCount--; - - SaveMessageDatabase(); - SendARQMail(conn); // See if any more - close if not - } - } - else - conn->ARQClearCount = 10; - } - } - } -#ifndef LINBPQ - for (Cons = ConsHeader[0]; Cons; Cons = Cons->next) - { - if (Cons->Console) - Flush(Cons->Console); - } -#endif -} - - -void Flush(CIRCUIT * conn) -{ - int tosend, len, sent; - - // Try to send data to user. May be stopped by user paging or node flow control - - // UCHAR * OutputQueue; // Messages to user - // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message - // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. - - // BOOL Paging; // Set if user wants paging - // int LinesSent; // Count when paging - // int PageLen; // Lines per page - - - if (conn->OutputQueue == NULL) - { - // Nothing to send. If Close after Flush is set, disconnect - - if (conn->CloseAfterFlush) - { - conn->CloseAfterFlush--; - - if (conn->CloseAfterFlush) - return; - - Disconnect(conn->BPQStream); - conn->ErrorCount = 0; - } - - return; // Nothing to send - } - tosend = conn->OutputQueueLength - conn->OutputGetPointer; - - sent=0; - - while (tosend > 0) - { - if (TXCount(conn->BPQStream) > 15) - return; // Busy - - if (conn->BBSFlags & SYSOPCHAT) // Suspend queued output while sysop chatting - return; - - if (conn->Paging && (conn->LinesSent >= conn->PageLen)) - return; - - if (tosend <= conn->paclen) - len=tosend; - else - len=conn->paclen; - - GetSemaphore(&OutputSEM, 0); - - if (conn->Paging) - { - // look for CR chars in message to send. Increment LinesSent, and stop if at limit - - UCHAR * ptr1 = &conn->OutputQueue[conn->OutputGetPointer]; - UCHAR * ptr2; - int lenleft = len; - - ptr2 = memchr(ptr1, 0x0d, len); - - while (ptr2) - { - conn->LinesSent++; - ptr2++; - lenleft = len - (int)(ptr2 - ptr1); - - if (conn->LinesSent >= conn->PageLen) - { - len = (int)(ptr2 - &conn->OutputQueue[conn->OutputGetPointer]); - - SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); - conn->OutputGetPointer+=len; - tosend-=len; - SendUnbuffered(conn->BPQStream, "bort, Continue..>", 25); - FreeSemaphore(&OutputSEM); - return; - - } - ptr2 = memchr(ptr2, 0x0d, lenleft); - } - } - - SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); - - conn->OutputGetPointer+=len; - - FreeSemaphore(&OutputSEM); - - tosend-=len; - sent++; - - if (sent > 15) - return; - } - - // All Sent. Free buffers and reset pointers - - conn->LinesSent = 0; - - ClearQueue(conn); -} - -VOID ClearQueue(ConnectionInfo * conn) -{ - if (conn->OutputQueue == NULL) - return; - - GetSemaphore(&OutputSEM, 0); - - free(conn->OutputQueue); - - conn->OutputQueue=NULL; - conn->OutputGetPointer=0; - conn->OutputQueueLength=0; - - FreeSemaphore(&OutputSEM); -} - - - -VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB) -{ - struct UserInfo * user; - - Msg->status='K'; - Msg->datechanged=time(NULL); - - // Remove any forwarding references - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - { - for (user = BBSChain; user; user = user->BBSNext) - { - if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) - { - user->ForwardingInfo->MsgCount--; - clear_fwd_bit(Msg->fbbs, user->BBSNumber); - } - } - } - if (SaveDB) - SaveMessageDatabase(); - RebuildNNTPList(); -} - -void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - int msgno=-1; - struct MsgInfo * Msg; - - while (Arg1) - { - msgno = atoi(Arg1); - - if (msgno > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return; - } - - Msg = GetMsgFromNumber(msgno); - - if (Msg == NULL) - { - nodeprintf(conn, "Message %d not found\r", msgno); - goto Next; - } - - if (Msg->type != 'T') - { - nodeprintf(conn, "Message %d not an NTS Message\r", msgno); - goto Next; - } - - if (Msg->status == 'N') - nodeprintf(conn, "Warning - Message has status N\r"); - - Msg->status = 'D'; - Msg->datechanged=time(NULL); - SaveMessageDatabase(); - - nodeprintf(conn, "Message #%d Flagged as Delivered\r", msgno); - Next: - Arg1 = strtok_s(NULL, " \r", &Context); - } - - return; -} - -void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - int msgno=-1; - int i; - struct MsgInfo * Msg; - - // Param is either ALL or a list of numbers - - if (Arg1 == NULL) - { - nodeprintf(conn, "No message number\r"); - return; - } - - if (_stricmp(Arg1, "ALL") == 0) - { - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - - if (Msg->status == 'H') - { - if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) - Msg->status = '$'; // Has forwarding - else - Msg->status = 'N'; - - nodeprintf(conn, "Message #%d Unheld\r", Msg->number); - } - } - return; - } - - while (Arg1) - { - msgno = atoi(Arg1); - Msg = GetMsgFromNumber(msgno); - - if (Msg) - { - if (Msg->status == 'H') - { - if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) - Msg->status = '$'; // Has forwarding - else - Msg->status = 'N'; - - nodeprintf(conn, "Message #%d Unheld\r", msgno); - } - else - { - nodeprintf(conn, "Message #%d was not held\r", msgno); - } - } - else - nodeprintf(conn, "Message #%d not found\r", msgno); - - Arg1 = strtok_s(NULL, " \r", &Context); - } - - return; -} - -void DoKillCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - int msgno=-1; - int i; - struct MsgInfo * Msg; - - switch (toupper(Cmd[1])) - { - - case 0: // Just K - - while (Arg1) - { - msgno = atoi(Arg1); - KillMessage(conn, user, msgno); - - Arg1 = strtok_s(NULL, " \r", &Context); - } - - SaveMessageDatabase(); - return; - - case 'M': // Kill Mine - - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - - if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) - { - if (Msg->type == 'P' && Msg->status == 'Y') - { - FlagAsKilled(Msg, FALSE); - nodeprintf(conn, "Message #%d Killed\r", Msg->number); - } - } - } - - SaveMessageDatabase(); - return; - - case 'H': // Kill Held - - if (conn->sysop) - { - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - - if (Msg->status == 'H') - { - FlagAsKilled(Msg, FALSE); - nodeprintf(conn, "Message #%d Killed\r", Msg->number); - } - } - } - SaveMessageDatabase(); - return; - - case '>': // K> - Kill to - - if (conn->sysop) - { - if (Arg1) - if (KillMessagesTo(conn, user, Arg1) == 0) - BBSputs(conn, "No Messages found\r"); - - return; - } - - case '<': - - if (conn->sysop) - { - if (Arg1) - if (KillMessagesFrom(conn, user, Arg1) == 0); - BBSputs(conn, "No Messages found\r"); - - return; - } - } - - nodeprintf(conn, "*** Error: Invalid Kill option %c\r", Cmd[1]); - - return; - -} - -int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call) -{ - int i, Msgs = 0; - struct MsgInfo * Msg; - - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - if (Msg->status != 'K' && _stricmp(Msg->to, Call) == 0) - { - Msgs++; - KillMessage(conn, user, MsgHddrPtr[i]->number); - } - } - - SaveMessageDatabase(); - return(Msgs); -} - -int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call) -{ - int i, Msgs = 0; - struct MsgInfo * Msg; - - - for (i=NumberofMessages; i>0; i--) - { - Msg = MsgHddrPtr[i]; - if (Msg->status != 'K' && _stricmp(Msg->from, Call) == 0) - { - Msgs++; - KillMessage(conn, user, MsgHddrPtr[i]->number); - } - } - - SaveMessageDatabase(); - return(Msgs); -} - -BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg) -{ - if (SYSOP || (Msg->type == 'T' && UserCantKillT == FALSE)) - return TRUE; - - if (Msg->type == 'P') - if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) - return TRUE; - - if (Msg->type == 'B') - if (_stricmp(Msg->from, Call) == 0) - return TRUE; - - return FALSE; -} - -void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) -{ - struct MsgInfo * Msg; - - Msg = GetMsgFromNumber(msgno); - - if (Msg == NULL || Msg->status == 'K') - { - nodeprintf(conn, "Message %d not found\r", msgno); - return; - } - - if (OkToKillMessage(conn->sysop, user->Call, Msg)) - { - FlagAsKilled(Msg, FALSE); - nodeprintf(conn, "Message #%d Killed\r", msgno); - } - else - nodeprintf(conn, "Not your message\r"); -} - - -BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp) -{ - char FullFrom[80]; - char FullTo[80]; - - strcpy(FullFrom, Msg->from); - - if ((_stricmp(Msg->from, "RMS:") == 0) || (_stricmp(Msg->from, "SMTP:") == 0) || - Temp->SendFullFrom || (_stricmp(Msg->emailfrom, "@winlink.org") == 0)) - strcat(FullFrom, Msg->emailfrom); - - if (_stricmp(Msg->to, "RMS") == 0) - { - sprintf(FullTo, "RMS:%s", Msg->via); - nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", - Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); - } - else - - if (Msg->to[0] == 0 && Msg->via[0] != 0) - { - sprintf(FullTo, "smtp:%s", Msg->via); - nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", - Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); - } - - else - if (Msg->via[0] != 0) - { - char Via[80]; - strcpy(Via, Msg->via); - strlop(Via, '.'); // Only show first part of via - nodeprintf(conn, "%-6d %s %c%c %5d %-7s@%-6s %-6s %-s\r", - Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, Via, FullFrom, Msg->title); - } - else - nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", - Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, FullFrom, Msg->title); - - // if paging, stop two before page lengh. This lets us send the continue prompt, save status - // and exit without triggering the system paging code. We can then read a message then resume listing - - if (Temp->ListActive && conn->Paging) - { - Temp->LinesSent++; - - if ((Temp->LinesSent + 1) >= conn->PageLen) - { - nodeprintf(conn, "bort, , = Continue..>"); - Temp->LastListedInPagedMode = Msg->number; - Temp->ListSuspended = TRUE; - return TRUE; - } - } - - return FALSE; -} - -void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context) -{ - struct TempUserInfo * Temp = user->Temp; - struct MsgInfo * Msg; - - // Allow compound selection, eg LTN or LFP - - // types P N T - // Options LL LR L< L> L@ LM LC (L* used internally for just L, ie List New - // Status N Y H F K D - - // Allowing options in any order complicates paging. May be best to parse options once and restore if paging. - - Temp->ListActive = TRUE; - Temp->LinesSent = 0; - - if (Resuming) - { - // Entered after a paging pause. Selection fields are already set up - - // We have reentered list command after a pause. The next message to list is in Temp->LastListedInPagedMode - -// Start = Temp->LastListedInPagedMode; - Temp->ListSuspended = FALSE; - } - else - { - Temp->ListRangeEnd = LatestMsg; - Temp->ListRangeStart = 1; - Temp->LLCount = 0; - Temp->SendFullFrom = 0; - Temp->ListType = 0; - Temp->ListStatus = 0; - Temp->ListSelector = 0; - Temp->UpdateLatest = 0; - Temp->LastListParams[0] = 0; - Temp->IncludeKilled = 1; // SYSOP include Killed except LM - - //Analyse L params. - - _strupr(Cmd); - - if (strcmp(Cmd, "LC") == 0) // List Bull Categories - { - ListCategories(conn); - return; - } - - // if command is just L or LR start from last listed - - if (Arg1 == NULL) - { - if (strcmp(Cmd, "L") == 0 || strcmp(Cmd, "LR") == 0) - { - if (LatestMsg == conn->lastmsg) - { - BBSputs(conn, "No New Messages\r"); - return; - } - - Temp->UpdateLatest = 1; - Temp->ListRangeStart = conn->lastmsg; - } - } - - if (strchr(Cmd, 'V')) // Verbose - Temp->SendFullFrom = 'V'; - - if (strchr(Cmd, 'R')) - Temp->ListDirn = 'R'; - else - Temp->ListDirn = '*'; // Default newest first - - Cmd++; // skip L - - if (strchr(Cmd, 'T')) - Temp->ListType = 'T'; - else if (strchr(Cmd, 'P')) - Temp->ListType = 'P'; - else if (strchr(Cmd, 'B')) - Temp->ListType = 'B'; - - if (strchr(Cmd, 'N')) - Temp->ListStatus = 'N'; - else if (strchr(Cmd, 'Y')) - Temp->ListStatus = 'Y'; - else if (strchr(Cmd, 'F')) - Temp->ListStatus = 'F'; - else if (strchr(Cmd, '$')) - Temp->ListStatus = '$'; - else if (strchr(Cmd, 'H')) - Temp->ListStatus = 'H'; - else if (strchr(Cmd, 'K')) - Temp->ListStatus = 'K'; - else if (strchr(Cmd, 'D')) - Temp->ListStatus = 'D'; - - // H or K only by Sysop - - switch (Temp->ListStatus) - { - case 'K': - case 'H': // List Status - - if (conn->sysop) - break; - - BBSputs(conn, "LH or LK can only be used by SYSOP\r"); - return; - } - - if (strchr(Cmd, '<')) - Temp->ListSelector = '<'; - else if (strchr(Cmd, '>')) - Temp->ListSelector = '>'; - else if (strchr(Cmd, '@')) - Temp->ListSelector = '@'; - else if (strchr(Cmd, 'M')) - { - Temp->ListSelector = 'M'; - Temp->IncludeKilled = FALSE; - } - - // Param could be single number, number range or call - - if (Arg1) - { - if (strchr(Cmd, 'L')) // List Last - { - // Param is number - - if (Arg1) - Temp->LLCount = atoi(Arg1); - } - else - { - // Range nnn-nnn or single value or callsign - - char * Arg2, * Arg3, * Range; - char seps[] = " \t\r"; - UINT From=LatestMsg, To=0; - - Arg2 = strtok_s(NULL, seps, &Context); - Arg3 = strtok_s(NULL, seps, &Context); - - if (Temp->ListSelector && Temp->ListSelector != 'M') - { - // < > or @ - first param is callsign - - strcpy(Temp->LastListParams, Arg1); - - // Just possible number range - - Arg1 = Arg2; - Arg2 = Arg3; - Arg3 = strtok_s(NULL, seps, &Context); - } - - if (Arg1) - { - Range = strchr(Arg1, '-'); - - // A number could be a Numeric Bull Dest (eg 44) - // I think this can only resaonably be > - - if (isdigits(Arg1)) - To = From = atoi(Arg1); - - if (Arg2) - From = atoi(Arg2); - else - { - if (Range) - { - Arg3 = strlop(Arg1, '-'); - - To = atoi(Arg1); - - if (Arg3 && Arg3[0]) - From = atoi(Arg3); - else - From = LatestMsg; - } - } - if (From > 100000 || To > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return; - } - Temp->ListRangeStart = To; - Temp->ListRangeEnd = From; - } - } - } - } - - // Run through all messages (either forwards or backwards) and list any that match all selection criteria - - while (1) - { - if (Temp->ListDirn == 'R') - Msg = GetMsgFromNumber(Temp->ListRangeStart); - else - Msg = GetMsgFromNumber(Temp->ListRangeEnd); - - - if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop, Temp->IncludeKilled)) // Check if user is allowed to list this message - { - // Check filters - - if (Temp->ListStatus && Temp->ListStatus != Msg->status) - goto skip; - - if (Temp->ListType && Temp->ListType != Msg->type) - goto skip; - - if (Temp->ListSelector == '<') - if (_stricmp(Msg->from, Temp->LastListParams) != 0) - goto skip; - - if (Temp->ListSelector == '>') - if (_stricmp(Msg->to, Temp->LastListParams) != 0) - goto skip; - - if (Temp->ListSelector == '@') - if (_memicmp(Msg->via, Temp->LastListParams, strlen(Temp->LastListParams)) != 0 && - (_stricmp(Temp->LastListParams, "SMTP:") != 0 || Msg->to[0] != 0)) - goto skip; - - if (Temp->ListSelector == 'M') - if (_stricmp(Msg->to, user->Call) != 0 && - (_stricmp(Msg->to, "SYSOP") != 0 || ((user->flags & F_SYSOP_IN_LM) == 0))) - - goto skip; - - if (ListMessage(Msg, conn, Temp)) - { - if (Temp->ListDirn == 'R') - Temp->ListRangeStart++; - else - Temp->ListRangeEnd--; - - return; // Hit page limit - } - - if (Temp->LLCount) - { - Temp->LLCount--; - if (Temp->LLCount == 0) - return; // LL count reached - } -skip:; - } - - if (Temp->ListRangeStart == Temp->ListRangeEnd) - { - // if using L or LR (list new) update last listed field - - if (Temp->UpdateLatest) - conn->lastmsg = LatestMsg; - - return; - } - - if (Temp->ListDirn == 'R') - Temp->ListRangeStart++; - else - Temp->ListRangeEnd--; - - if (Temp->ListRangeStart > 100000 || Temp->ListRangeEnd < 0) // Loop protection! - return; - - } - -/* - - switch (Cmd[0]) - { - - case '*': // Just L - case 'R': // LR = List Reverse - - if (Arg1) - { - // Range nnn-nnn or single value - - char * Arg2, * Arg3; - char * Context; - char seps[] = " -\t\r"; - UINT From=LatestMsg, To=0; - char * Range = strchr(Arg1, '-'); - - Arg2 = strtok_s(Arg1, seps, &Context); - Arg3 = strtok_s(NULL, seps, &Context); - - if (Arg2) - To = From = atoi(Arg2); - - if (Arg3) - From = atoi(Arg3); - else - if (Range) - From = LatestMsg; - - if (From > 100000 || To > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return; - } - - if (Cmd[1] == 'R') - { - if (Start) - To = Start + 1; - - ListMessagesInRangeForwards(conn, user, user->Call, From, To, Temp->SendFullFrom); - } - else - { - if (Start) - From = Start - 1; - - ListMessagesInRange(conn, user, user->Call, From, To, Temp->SendFullFrom); - } - } - else - - if (LatestMsg == conn->lastmsg) - BBSputs(conn, "No New Messages\r"); - else if (Cmd[1] == 'R') - ListMessagesInRangeForwards(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); - else - ListMessagesInRange(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); - - conn->lastmsg = LatestMsg; - - return; - - - case 'L': // List Last - - if (Arg1) - { - int i = atoi(Arg1); - int m = NumberofMessages; - - if (Resuming) - i = Temp->LLCount; - else - Temp->LLCount = i; - - for (; i>0 && m != 0; i--) - { - m = GetUserMsg(m, user->Call, conn->sysop); - - if (m > 0) - { - if (Start && MsgHddrPtr[m]->number >= Start) - { - m--; - i++; - continue; - } - - Temp->LLCount--; - - if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) - return; // Hit page limit - m--; - } - } - } - return; - - case 'M': // LM - List Mine - - if (ListMessagesTo(conn, user, user->Call, SendFullFrom, Start) == 0) - BBSputs(conn, "No Messages found\r"); - return; - - case '>': // L> - List to - - if (Arg1) - if (ListMessagesTo(conn, user, Arg1, SendFullFrom, Start) == 0) - BBSputs(conn, "No Messages found\r"); - - - return; - - case '<': - - if (Arg1) - if (ListMessagesFrom(conn, user, Arg1, SendFullFrom, Start) == 0) - BBSputs(conn, "No Messages found\r"); - - return; - - case '@': - - if (Arg1) - if (ListMessagesAT(conn, user, Arg1, SendFullFrom, Start) == 0) - BBSputs(conn, "No Messages found\r"); - - return; - - case 'N': - case 'Y': - case 'F': - case '$': - case 'D': // Delivered NTS Traffic can be listed by anyone - { - int m = NumberofMessages; - - while (m > 0) - { - m = GetUserMsg(m, user->Call, conn->sysop); - - if (m > 0) - { - if (Start && MsgHddrPtr[m]->number >= Start) - { - m--; - continue; - } - - if (Temp->ListType) - { - if (MsgHddrPtr[m]->status == Cmd[1] && MsgHddrPtr[m]->type == Temp->ListType) - if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) - return; // Hit page limit - } - else - { - if (MsgHddrPtr[m]->status == toupper(Cmd[1])) - if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) - return; // Hit page limit - } - m--; - } - } - } - return; - - case 'K': - case 'H': // List Status - - if (conn->sysop) - { - int i, Msgs = Start; - - for (i=NumberofMessages; i>0; i--) - { - if (Start && MsgHddrPtr[i]->number >= Start) - continue; - - if (MsgHddrPtr[i]->status == toupper(Cmd[1])) - { - Msgs++; - if (ListMessage(MsgHddrPtr[i], conn, SendFullFrom)) - return; // Hit page limit - - } - } - - if (Msgs == 0) - BBSputs(conn, "No Messages found\r"); - } - else - BBSputs(conn, "LH or LK can only be used by SYSOP\r"); - - return; - - case 'C': - { - struct NNTPRec * ptr = FirstNNTPRec; - char Cat[100]; - char NextCat[100]; - int Line = 0; - int Count; - - while (ptr) - { - // if the next name is the same, combine counts - - strcpy(Cat, ptr->NewsGroup); - strlop(Cat, '.'); - Count = ptr->Count; - Catloop: - if (ptr->Next) - { - strcpy(NextCat, ptr->Next->NewsGroup); - strlop(NextCat, '.'); - if (strcmp(Cat, NextCat) == 0) - { - ptr = ptr->Next; - Count += ptr->Count; - goto Catloop; - } - } - - nodeprintf(conn, "%-6s %-3d", Cat, Count); - Line += 10; - if (Line > 80) - { - Line = 0; - nodeprintf(conn, "\r"); - } - - ptr = ptr->Next; - } - - if (Line) - nodeprintf(conn, "\r\r"); - else - nodeprintf(conn, "\r"); - - return; - } - } - - // Could be P B or T if specified without a status - - switch (Temp->ListType) - { - case 'P': - case 'B': - case 'T': // NTS Traffic can be listed by anyone - { - int m = NumberofMessages; - - while (m > 0) - { - m = GetUserMsg(m, user->Call, conn->sysop); - - if (m > 0) - { - if (Start && MsgHddrPtr[m]->number >= Start) - { - m--; - continue; - } - - if (MsgHddrPtr[m]->type == Temp->ListType) - if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) - return; // Hit page limit - m--; - } - } - - return; - } - } - -*/ - nodeprintf(conn, "*** Error: Invalid List option %c\r", Cmd[1]); - -} - -void ListCategories(ConnectionInfo * conn) -{ - // list bull categories - struct NNTPRec * ptr = FirstNNTPRec; - char Cat[100]; - char NextCat[100]; - int Line = 0; - int Count; - - while (ptr) - { - // if the next name is the same, combine counts - - strcpy(Cat, ptr->NewsGroup); - strlop(Cat, '.'); - Count = ptr->Count; -Catloop: - if (ptr->Next) - { - strcpy(NextCat, ptr->Next->NewsGroup); - strlop(NextCat, '.'); - if (strcmp(Cat, NextCat) == 0) - { - ptr = ptr->Next; - Count += ptr->Count; - goto Catloop; - } - } - - nodeprintf(conn, "%-6s %-3d", Cat, Count); - Line += 10; - if (Line > 80) - { - Line = 0; - nodeprintf(conn, "\r"); - } - - ptr = ptr->Next; - } - - if (Line) - nodeprintf(conn, "\r\r"); - else - nodeprintf(conn, "\r"); - - return; -} - -/* -int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) -{ - int i, Msgs = Start; - - for (i=NumberofMessages; i>0; i--) - { - if (MsgHddrPtr[i]->status == 'K') - continue; - - if (Start && MsgHddrPtr[i]->number >= Start) - continue; - - if ((_stricmp(MsgHddrPtr[i]->to, Call) == 0) || - ((conn->sysop) && _stricmp(Call, SYSOPCall) == 0 && - _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0 && (user->flags & F_SYSOP_IN_LM))) - { - Msgs++; - if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) - break; // Hit page limit - } - } - - return(Msgs); -} - -int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) -{ - int i, Msgs = 0; - - for (i=NumberofMessages; i>0; i--) - { - if (MsgHddrPtr[i]->status == 'K') - continue; - - if (Start && MsgHddrPtr[i]->number >= Start) - continue; - - if (_stricmp(MsgHddrPtr[i]->from, Call) == 0) - { - Msgs++; - if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) - return Msgs; // Hit page limit - - } - } - - return(Msgs); -} - -int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom,int Start) -{ - int i, Msgs = 0; - - for (i=NumberofMessages; i>0; i--) - { - if (MsgHddrPtr[i]->status == 'K') - continue; - - if (Start && MsgHddrPtr[i]->number >= Start) - continue; - - if (_memicmp(MsgHddrPtr[i]->via, Call, strlen(Call)) == 0 || - (_stricmp(Call, "SMTP:") == 0 && MsgHddrPtr[i]->to[0] == 0)) - { - Msgs++; - if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) - break; // Hit page limit - } - } - - return(Msgs); -} -*/ -int GetUserMsg(int m, char * Call, BOOL SYSOP) -{ - struct MsgInfo * Msg; - - // Get Next (usually backwards) message which should be shown to this user - // ie Not Deleted, and not Private unless to or from Call - - do - { - Msg=MsgHddrPtr[m]; - - if (SYSOP) return m; // Sysop can list or read anything - - if (Msg->status != 'K') - { - - if (Msg->status != 'H') - { - if (Msg->type == 'B' || Msg->type == 'T') return m; - - if (Msg->type == 'P') - { - if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) - return m; - } - } - } - - m--; - - } while (m> 0); - - return 0; -} - - -BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled) -{ - // Return TRUE if user is allowed to read message - - if (Msg->status == 'K' && IncludeKilled == 0) - return FALSE; - - if (SYSOP) - return TRUE; // Sysop can list or read anything - - if ((Msg->status != 'K') && (Msg->status != 'H')) - { - if (Msg->type == 'B' || Msg->type == 'T') return TRUE; - - if (Msg->type == 'P') - { - if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) - return TRUE; - } - } - - return FALSE; -} -/* -int GetUserMsgForwards(int m, char * Call, BOOL SYSOP) -{ - struct MsgInfo * Msg; - - // Get Next (usually backwards) message which should be shown to this user - // ie Not Deleted, and not Private unless to or from Call - - do - { - Msg=MsgHddrPtr[m]; - - if (Msg->status != 'K') - { - if (SYSOP) return m; // Sysop can list or read anything - - if (Msg->status != 'H') - { - if (Msg->type == 'B' || Msg->type == 'T') return m; - - if (Msg->type == 'P') - { - if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) - return m; - } - } - } - - m++; - - } while (m <= NumberofMessages); - - return 0; - -} - - -void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom) -{ - int m; - struct MsgInfo * Msg; - - for (m = Start; m >= End; m--) - { - Msg = GetMsgFromNumber(m); - - if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) - if (ListMessage(Msg, conn, Temp->SendFullFrom)) - return; // Hit page limit - - } -} - - -void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int End, int Start, BOOL SendFullFrom) -{ - int m; - struct MsgInfo * Msg; - - for (m = Start; m <= End; m++) - { - Msg = GetMsgFromNumber(m); - - if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) - if (ListMessage(Msg, conn, Temp->SendFullFrom)) - return; // Hit page limit - } -} -*/ - -void DoReadCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - int msgno=-1; - int i; - struct MsgInfo * Msg; - - - switch (toupper(Cmd[1])) - { - case 0: // Just R - - while (Arg1) - { - msgno = atoi(Arg1); - if (msgno > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return; - } - - ReadMessage(conn, user, msgno); - Arg1 = strtok_s(NULL, " \r", &Context); - } - - return; - - case 'M': // Read Mine (Unread Messages) - - if (toupper(Cmd[2]) == 'R') - { - for (i = 1; i <= NumberofMessages; i++) - { - Msg = MsgHddrPtr[i]; - - if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) - if (Msg->status == 'N') - ReadMessage(conn, user, Msg->number); - } - } - else - { - for (i = NumberofMessages; i > 0; i--) - { - Msg = MsgHddrPtr[i]; - - if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) - if (Msg->status == 'N') - ReadMessage(conn, user, Msg->number); - } - } - - return; - } - - nodeprintf(conn, "*** Error: Invalid Read option %c\r", Cmd[1]); - - return; -} - -int RemoveLF(char * Message, int len) -{ - // Remove lf chars and nulls - - char * ptr1, * ptr2; - - ptr1 = ptr2 = Message; - - while (len-- > 0) - { - while (*ptr1 == 0 && len) - { - ptr1++; - len--; - } - - *ptr2 = *ptr1; - - if (*ptr1 == '\r') - if (*(ptr1+1) == '\n') - { - ptr1++; - len--; - } - ptr1++; - ptr2++; - } - - return (int)(ptr2 - Message); -} - - - -int RemoveNulls(char * Message, int len) -{ - // Remove nulls - - char * ptr1, * ptr2; - - ptr1 = ptr2 = Message; - - while (len-- > 0) - { - while (*ptr1 == 0 && len) - { - ptr1++; - len--; - } - - *ptr2 = *ptr1; - - ptr1++; - ptr2++; - } - - return (int)(ptr2 - Message); -} - -void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) -{ - struct MsgInfo * Msg; - char * MsgBytes, * Save; - char FullTo[100]; - int Index = 0; - - Msg = GetMsgFromNumber(msgno); - - if (Msg == NULL) - { - nodeprintf(conn, "Message %d not found\r", msgno); - return; - } - - if (!CheckUserMsg(Msg, user->Call, conn->sysop, TRUE)) - { - nodeprintf(conn, "Message %d not for you\r", msgno); - return; - } - - if (_stricmp(Msg->to, "RMS") == 0) - sprintf(FullTo, "RMS:%s", Msg->via); - else - if (Msg->to[0] == 0) - sprintf(FullTo, "smtp:%s", Msg->via); - else - strcpy(FullTo, Msg->to); - - - nodeprintf(conn, "From: %s%s\rTo: %s\rType/Status: %c%c\rDate/Time: %s\rBid: %s\rTitle: %s\r\r", - Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); - - MsgBytes = Save = ReadMessageFile(msgno); - - if (Msg->type == 'P') - Index = PMSG; - else if (Msg->type == 'B') - Index = BMSG; - else if (Msg->type == 'T') - Index = TMSG; - - if (MsgBytes) - { - int Length = Msg->length; - - if (Msg->B2Flags & B2Msg) - { - char * ptr; - - // if message has attachments, display them if plain text - - if (Msg->B2Flags & Attachments) - { - char * FileName[100]; - int FileLen[100]; - int Files = 0; - int BodyLen, NewLen; - int i; - char *ptr2; - char Msg[512]; - int Len; - - ptr = MsgBytes; - - Len = sprintf(Msg, "Message has Attachments\r\r"); - QueueMsg(conn, Msg, Len); - - while(*ptr != 13) - { - ptr2 = strchr(ptr, 10); // Find CR - - if (memcmp(ptr, "Body: ", 6) == 0) - { - BodyLen = atoi(&ptr[6]); - } - - if (memcmp(ptr, "File: ", 6) == 0) - { - char * ptr1 = strchr(&ptr[6], ' '); // Find Space - - FileLen[Files] = atoi(&ptr[6]); - - FileName[Files++] = &ptr1[1]; - *(ptr2 - 1) = 0; - } - - ptr = ptr2; - ptr++; - } - - ptr += 2; // Over Blank Line and Separator - - NewLen = RemoveLF(ptr, BodyLen); - - QueueMsg(conn, ptr, NewLen); // Display Body - - ptr += BodyLen + 2; // to first file - - for (i = 0; i < Files; i++) - { - char Msg[512]; - int Len, n; - char * p = ptr; - char c; - - // Check if message is probably binary - - int BinCount = 0; - - NewLen = RemoveLF(ptr, FileLen[i]); // Removes LF agter CR but not on its own - - for (n = 0; n < NewLen; n++) - { - c = *p; - - if (c == 10) - *p = 13; - - if (c==0 || (c & 128)) - BinCount++; - - p++; - - } - - if (BinCount > NewLen/10) - { - // File is probably Binary - - Len = sprintf(Msg, "\rAttachment %s is a binary file\r", FileName[i]); - QueueMsg(conn, Msg, Len); - } - else - { - Len = sprintf(Msg, "\rAttachment %s\r\r", FileName[i]); - QueueMsg(conn, Msg, Len); - - user->Total.MsgsSent[Index] ++; - user->Total.BytesForwardedOut[Index] += NewLen; - - QueueMsg(conn, ptr, NewLen); - } - - ptr += FileLen[i]; - ptr +=2; // Over separator - } - goto sendEOM; - } - - // Remove B2 Headers (up to the File: Line) - - ptr = strstr(MsgBytes, "Body:"); - - if (ptr) - { - MsgBytes = ptr; - Length = (int)strlen(ptr); - } - } - - // Remove lf chars - - Length = RemoveLF(MsgBytes, Length); - - user->Total.MsgsSent[Index] ++; - user->Total.BytesForwardedOut[Index] += Length; - - QueueMsg(conn, MsgBytes, Length); - -sendEOM: - - free(Save); - - nodeprintf(conn, "\r\r[End of Message #%d from %s%s]\r", msgno, Msg->from, Msg->emailfrom); - - if ((_stricmp(Msg->to, user->Call) == 0) || ((conn->sysop) && (_stricmp(Msg->to, "SYSOP") == 0))) - { - if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) - { - if (Msg->status != 'Y') - { - Msg->status = 'Y'; - Msg->datechanged=time(NULL); - SaveMessageDatabase(); - } - } - } - } - else - { - nodeprintf(conn, "File for Message %d not found\r", msgno); - } -} - struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop) - { - int m=NumberofMessages; - - struct MsgInfo * Msg; - - do - { - m = GetUserMsg(m, Call, sysop); - - if (m == 0) - return NULL; - - Msg=MsgHddrPtr[m]; - - if (Msg->number == msgno) - return Msg; - - m--; - - } while (m> 0); - - return NULL; - -} - - -char * ReadInfoFile(char * File) -{ - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char * MsgBytes; - struct stat STAT; - char * ptr1 = 0, * ptr2; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BaseDir, File); - - if (stat(MsgFile, &STAT) == -1) - return NULL; - - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile == NULL) - return NULL; - - MsgBytes=malloc(FileSize+1); - - fread(MsgBytes, 1, FileSize, hFile); - - fclose(hFile); - - MsgBytes[FileSize] = 0; - - ptr1 = MsgBytes; - - // Replace LF or CRLF with CR - - // First remove cr from crlf - - while(ptr2 = strstr(ptr1, "\r\n")) - { - memmove(ptr2, ptr2 + 1, strlen(ptr2)); - } - - // Now replace lf with cr - - ptr1 = MsgBytes; - - while (*ptr1) - { - if (*ptr1 == '\n') - *(ptr1) = '\r'; - - ptr1++; - } - - return MsgBytes; -} - -char * FormatDateAndTime(time_t Datim, BOOL DateOnly) -{ - struct tm *tm; - static char Date[]="xx-xxx hh:mmZ"; - - tm = gmtime(&Datim); - - if (tm) - sprintf_s(Date, sizeof(Date), "%02d-%3s %02d:%02dZ", - tm->tm_mday, month[tm->tm_mon], tm->tm_hour, tm->tm_min); - - if (DateOnly) - { - Date[6]=0; - return Date; - } - - return Date; -} - -BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); - - -BOOL DoSendCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) -{ - // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY - - char * From = NULL; - char * BID = NULL; - char * ATBBS = NULL; - char seps[] = " \t\r"; - struct MsgInfo * OldMsg; - char OldTitle[62]; - char NewTitle[62]; - char To[100]= ""; - int msgno; - - if (Cmd[1] == 0) Cmd[1] ='P'; // Just S means SP - - switch (toupper(Cmd[1])) - { - case 'B': - - if (RefuseBulls) - { - nodeprintf(conn, "*** Error: This system doesn't allow sending Bulls\r"); - return FALSE; - } - - if (user->flags & F_NOBULLS) - { - nodeprintf(conn, "*** Error: You are not allowed to send Bulls\r"); - return FALSE; - } - - - case 'P': - case 'T': - - if (Arg1 == NULL) - { - nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); - return FALSE; - } - - strcpy(To, Arg1); - - if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) - return FALSE; - - return CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL); - - case 'R': - - if (Arg1 == NULL) - { - nodeprintf(conn, "*** Error: Message Number is missing\r"); - return FALSE; - } - - msgno = atoi(Arg1); - - if (msgno > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return FALSE; - } - - OldMsg = FindMessage(user->Call, msgno, conn->sysop); - - if (OldMsg == NULL) - { - nodeprintf(conn, "Message %d not found\r", msgno); - return FALSE; - } - - Arg1=&OldMsg->from[0]; - - strcpy(To, Arg1); - - if (_stricmp(Arg1, "SMTP:") == 0 || _stricmp(Arg1, "RMS:") == 0 || OldMsg->emailfrom) - { - // SMTP message. Need to get the real sender from the message - - sprintf(To, "%s%s", Arg1, OldMsg->emailfrom); - } - - if (!DecodeSendParams(conn, "", &From, To, &ATBBS, &BID)) - return FALSE; - - strcpy(OldTitle, OldMsg->title); - - if (strlen(OldTitle) > 57) OldTitle[57] = 0; - - strcpy(NewTitle, "Re:"); - strcat(NewTitle, OldTitle); - - return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); - - return TRUE; - - case 'C': - - if (Arg1 == NULL) - { - nodeprintf(conn, "*** Error: Message Number is missing\r"); - return FALSE; - } - - msgno = atoi(Arg1); - - if (msgno > 100000) - { - BBSputs(conn, "Message Number too high\r"); - return FALSE; - } - - Arg1 = strtok_s(NULL, seps, &Context); - - if (Arg1 == NULL) - { - nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); - return FALSE; - } - - strcpy(To, Arg1); - - if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) - return FALSE; - - OldMsg = FindMessage(user->Call, msgno, conn->sysop); - - if (OldMsg == NULL) - { - nodeprintf(conn, "Message %d not found\r", msgno); - return FALSE; - } - - strcpy(OldTitle, OldMsg->title); - - if (strlen(OldTitle) > 56) OldTitle[56] = 0; - - strcpy(NewTitle, "Fwd:"); - strcat(NewTitle, OldTitle); - - conn->CopyBuffer = ReadMessageFile(msgno); - - return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); - } - - - nodeprintf(conn, "*** Error: Invalid Send option %c\r", Cmd[1]); - - return FALSE; -} - -char * CheckToAddress(CIRCUIT * conn, char * Addr) -{ - // Check one element of Multiple Address - - if (conn == NULL || !(conn->BBSFlags & BBS)) - { - // if a normal user, check that TO and/or AT are known and warn if not. - - if (_stricmp(Addr, "SYSOP") == 0) - { - return _strdup(Addr); - } - - if (SendBBStoSYSOPCall) - if (_stricmp(Addr, BBSName) == 0) - return _strdup(SYSOPCall); - - - if (strchr(Addr, '@') == 0) - { - // No routing, if not a user and not known to forwarding or WP warn - - struct UserInfo * ToUser = LookupCall(Addr); - - if (ToUser) - { - // Local User. If Home BBS is specified, use it - - if (ToUser->HomeBBS[0]) - { - char * NewAddr = malloc(250); - if (conn) - nodeprintf(conn, "Address %s - @%s added from HomeBBS\r", Addr, ToUser->HomeBBS); - sprintf(NewAddr, "%s@%s", Addr, ToUser->HomeBBS); - return NewAddr; - } - } - else - { - WPRecP WP = LookupWP(Addr); - - if (WP) - { - char * NewAddr = malloc(250); - - if (conn) - nodeprintf(conn, "Address %s - @%s added from WP\r", Addr, WP->first_homebbs); - sprintf(NewAddr, "%s@%s", Addr, WP->first_homebbs); - return NewAddr; - } - } - } - } - - // Check SMTP and RMS Addresses - - if ((_memicmp(Addr, "rms:", 4) == 0) || (_memicmp(Addr, "rms/", 4) == 0)) - { - Addr[3] = ':'; // Replace RMS/ with RMS: - - if (conn && !FindRMS()) - { - nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); - return FALSE; - } - } - else if ((_memicmp(Addr, "smtp:", 5) == 0) || (_memicmp(Addr, "smtp/", 5) == 0)) - { - Addr[4] = ':'; // Replace smpt/ with smtp: - - if (ISP_Gateway_Enabled) - { - if (conn && (conn->UserPointer->flags & F_EMAIL) == 0) - { - nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); - return FALSE; - } - } - else - { - if (conn) - nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); - return FALSE; - } - } - - return _strdup(Addr); -} - - -char Winlink[] = "WINLINK.ORG"; - -BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char *To, char ** ATBBS, char ** BID) -{ - char * ptr; - char seps[] = " \t\r"; - WPRecP WP; - char * ToCopy = _strdup(To); - int Len; - - conn->ToCount = 0; - - // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY - - // Having trailing ; will mess up parsing multiple addresses, so remove. - - while (To[strlen(To) - 1] == ';') - To[strlen(To) - 1] = 0; - - if (strchr(Context, ';') || strchr(To, ';')) - { - // Multiple Addresses - put address list back together - - char * p; - - To[strlen(To)] = ' '; - Context = To; - - while (p = strchr(Context, ';')) - { - // Multiple Addressees - - To = strtok_s(NULL, ";", &Context); - Len = (int)strlen(To); - conn->To = realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); - if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) - conn->ToCount++; - } - - To = strtok_s(NULL, seps, &Context); - - Len = (int)strlen(To); - conn->To=realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); - if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) - conn->ToCount++; - } - else - { - // Single Call - - // accept CALL!CALL for source routed message - - if (strchr(To, '@') == 0 && strchr(To, '!')) // Bang route without @ - { - char * bang = strchr(To, '!'); - - memmove(bang + 1, bang, strlen(bang)); // Move !call down one - - *ATBBS = strlop(To, '!');; - } - - // Accept call@call (without spaces) - but check for smtp addresses - - if (_memicmp(To, "smtp:", 5) != 0 && _memicmp(To, "rms:", 4) != 0 && _memicmp(To, "rms/", 4) != 0) - { - ptr = strchr(To, '@'); - - if (ptr) - { - // If looks like a valid email address, treat as such - - int tolen; - *ATBBS = strlop(To, '@'); - - strlop(To, '-'); // Cant have SSID on BBS Name - - tolen = (int)strlen(To); - - if (tolen > 6 || !CheckifPacket(*ATBBS)) - { - // Probably Email address. Add smtp: or rms: - - if (FindRMS() || strchr(*ATBBS, '!')) // have RMS or source route - sprintf(To, "rms:%s", ToCopy); - else if (ISP_Gateway_Enabled) - sprintf(To, "smtp:%s", ToCopy); - else if (isAMPRMsg(ToCopy)) - sprintf(To, "rms:%s", ToCopy); - - } - } - } - } - - free(ToCopy); - - // Look for Optional fields; - - ptr = strtok_s(NULL, seps, &Context); - - while (ptr) - { - if (strcmp(ptr, "@") == 0) - { - *ATBBS = _strupr(strtok_s(NULL, seps, &Context)); - } - else if(strcmp(ptr, "<") == 0) - { - *From = strtok_s(NULL, seps, &Context); - } - else if (ptr[0] == '$') - *BID = &ptr[1]; - else - { - nodeprintf(conn, "*** Error: Invalid Format\r"); - return FALSE; - } - ptr = strtok_s(NULL, seps, &Context); - } - - // Only allow < from a BBS - - if (*From) - { - if (!(conn->BBSFlags & BBS)) - { - nodeprintf(conn, "*** < can only be used by a BBS\r"); - return FALSE; - } - } - - if (!*From) - *From = conn->UserPointer->Call; - - if (!(conn->BBSFlags & BBS)) - { - // if a normal user, check that TO and/or AT are known and warn if not. - - if (_stricmp(To, "SYSOP") == 0) - { - conn->LocalMsg = TRUE; - return TRUE; - } - - if (!*ATBBS && conn->ToCount == 0) - { - // No routing, if not a user and not known to forwarding or WP warn - - struct UserInfo * ToUser = LookupCall(To); - - if (ToUser) - { - // Local User. If Home BBS is specified, use it - - if (ToUser->flags & F_RMSREDIRECT) - { - // sent to Winlink - - *ATBBS = Winlink; - nodeprintf(conn, "Redirecting to winlink.org\r", *ATBBS); - } - else if (ToUser->HomeBBS[0]) - { - *ATBBS = ToUser->HomeBBS; - nodeprintf(conn, "Address @%s added from HomeBBS\r", *ATBBS); - } - else - { - conn->LocalMsg = TRUE; - } - } - else - { - conn->LocalMsg = FALSE; - WP = LookupWP(To); - - if (WP) - { - *ATBBS = WP->first_homebbs; - nodeprintf(conn, "Address @%s added from WP\r", *ATBBS); - } - } - } - } - return TRUE; -} - -BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title) -{ - struct MsgInfo * Msg, * TestMsg; - char * via = NULL; - char * FromHA; - - // Create a temp msg header entry - - if (conn->ToCount) - { - } - else - { - if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType)) - { - if ((conn->BBSFlags & BBS)) - { - nodeprintf(conn, "NO - REJECTED\r"); - if (conn->BBSFlags & OUTWARDCONNECT) - nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward - else - nodeprintf(conn, ">\r"); - } - else - nodeprintf(conn, "*** Error - Message Filters prevent sending this message\r"); - - return FALSE; - } - } - - Msg = malloc(sizeof (struct MsgInfo)); - - if (Msg == 0) - { - CriticalErrorHandler("malloc failed for new message header"); - return FALSE; - } - - memset(Msg, 0, sizeof (struct MsgInfo)); - - conn->TempMsg = Msg; - - Msg->type = MsgType; - - if (conn->UserPointer->flags & F_HOLDMAIL) - Msg->status = 'H'; - else - Msg->status = 'N'; - - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - if (BID) - { - BIDRec * TempBID; - - // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message - // If we do, reject it. If not, accept it again. (do we need some loop protection ???) - - TempBID = LookupBID(BID); - - if (TempBID) - { - if (MsgType == 'B') - { - // Duplicate bid - - if ((conn->BBSFlags & BBS)) - { - nodeprintf(conn, "NO - BID\r"); - if (conn->BBSFlags & OUTWARDCONNECT) - nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward - else - nodeprintf(conn, ">\r"); - } - else - nodeprintf(conn, "*** Error- Duplicate BID\r"); - - return FALSE; - } - - TestMsg = GetMsgFromNumber(TempBID->u.msgno); - - // if the same TO we will assume the same message - - if (TestMsg && strcmp(TestMsg->to, ToCall) == 0) - { - // We have this message. If we have already forwarded it, we should accept it again - - if ((TestMsg->status == 'N') || (TestMsg->status == 'Y')|| (TestMsg->status == 'H')) - { - // Duplicate bid - - if ((conn->BBSFlags & BBS)) - { - nodeprintf(conn, "NO - BID\r"); - if (conn->BBSFlags & OUTWARDCONNECT) - nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward - else - nodeprintf(conn, ">\r"); - } - else - nodeprintf(conn, "*** Error- Duplicate BID\r"); - - return FALSE; - } - } - } - - if (strlen(BID) > 12) BID[12] = 0; - strcpy(Msg->bid, BID); - - // Save BID in temp list in case we are offered it again before completion - - TempBID = AllocateTempBIDRecord(); - strcpy(TempBID->BID, BID); - TempBID->u.conn = conn; - - } - - if (conn->ToCount) - { - } - else - { - if (_memicmp(ToCall, "rms:", 4) == 0) - { - if (!FindRMS()) - { - nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); - return FALSE; - } - - via=strlop(ToCall, ':'); - _strupr(ToCall); - } - else if (_memicmp(ToCall, "rms/", 4) == 0) - { - if (!FindRMS()) - { - nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); - return FALSE; - } - - via=strlop(ToCall, '/'); - _strupr(ToCall); - } - else if (_memicmp(ToCall, "smtp:", 5) == 0) - { - if (ISP_Gateway_Enabled) - { - if ((conn->UserPointer->flags & F_EMAIL) == 0) - { - nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); - return FALSE; - } - via=strlop(ToCall, ':'); - ToCall[0] = 0; - } - else - { - nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); - return FALSE; - } - } - else - { - _strupr(ToCall); - if (ATBBS) - via=_strupr(ATBBS); - } - - strlop(ToCall, '-'); // Remove any (illegal) ssid - if (strlen(ToCall) > 6) ToCall[6] = 0; - - strcpy(Msg->to, ToCall); - - if (SendBBStoSYSOPCall) - if (_stricmp(ToCall, BBSName) == 0) - strcpy(Msg->to, SYSOPCall); - - if (via) - { - if (strlen(via) > 40) via[40] = 0; - - strcpy(Msg->via, via); - } - - } // End of Multiple Dests - - // Look for HA in From (even if we shouldn't be getting it!) - - FromHA = strlop(From, '@'); - - - strlop(From, '-'); // Remove any (illegal) ssid - if (strlen(From) > 6) From[6] = 0; - strcpy(Msg->from, From); - - if (FromHA) - { - if (strlen(FromHA) > 39) FromHA[39] = 0; - Msg->emailfrom[0] = '@'; - strcpy(&Msg->emailfrom[1], _strupr(FromHA)); - } - - if (Title) // Only used by SR and SC - { - strcpy(Msg->title, Title); - conn->Flags |= GETTINGMESSAGE; - - // Create initial buffer of 10K. Expand if needed later - - conn->MailBuffer=malloc(10000); - conn->MailBufferSize=10000; - - nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); - return TRUE; - } - - if (conn->BBSFlags & FLARQMODE) - return TRUE; - - if (!(conn->BBSFlags & FBBCompressed)) - conn->Flags |= GETTINGTITLE; - - if (!(conn->BBSFlags & BBS)) - nodeprintf(conn, "Enter Title (only):\r"); - else - if (!(conn->BBSFlags & FBBForwarding)) - nodeprintf(conn, "OK\r"); - - return TRUE; -} - -VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int msglen) -{ - - conn->Flags &= ~GETTINGTITLE; - - if (msglen == 1) - { - nodeprintf(conn, "*** Message Cancelled\r"); - SendPrompt(conn, user); - return; - } - - if (msglen > 60) msglen = 60; - - Buffer[msglen-1] = 0; - - strcpy(conn->TempMsg->title, Buffer); - - // Create initial buffer of 10K. Expand if needed later - - conn->MailBuffer=malloc(10000); - conn->MailBufferSize=10000; - - if (conn->MailBuffer == NULL) - { - nodeprintf(conn, "Failed to create Message Buffer\r"); - return; - } - - conn->Flags |= GETTINGMESSAGE; - - if (!conn->BBSFlags & BBS) - nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); - -} - -VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int msglen) -{ - char * ptr2 = NULL; - - if (((msglen < 3) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3) == 0))) - { - int Index = 0; - - if (conn->TempMsg->type == 'P') - Index = PMSG; - else if (conn->TempMsg->type == 'B') - Index = BMSG; - else if (conn->TempMsg->type == 'T') - Index = TMSG; - - conn->Flags &= ~GETTINGMESSAGE; - - user->Total.MsgsReceived[Index]++; - user->Total.BytesForwardedIn[Index] += conn->TempMsg->length; - - if (conn->ToCount) - { - // Multiple recipients - - struct MsgInfo * Msg = conn->TempMsg; - int i; - struct MsgInfo * SaveMsg = Msg; - char * SaveBody = conn->MailBuffer; - int SaveMsgLen = Msg->length; - BOOL SentToRMS = FALSE; - int ToLen = 0; - char * ToString = zalloc(conn->ToCount * 100); - - // If no BID provided, allocate one - - if (Msg->bid[0] == 0) - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg + 1, BBSName); - - for (i = 0; i < conn->ToCount; i++) - { - char * Addr = conn->To[i]; - char * Via; - - if (_memicmp (Addr, "SMTP:", 5) == 0) - { - // For Email - - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 10); - memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); - - Msg->to[0] = 0; - strcpy(Msg->via, &Addr[5]); - - CreateMessageFromBuffer(conn); - continue; - } - - if (_memicmp (Addr, "RMS:", 4) == 0) - { - // Add to B2 Message for RMS - - Addr+=4; - - Via = strlop(Addr, '@'); - - if (Via && _stricmp(Via, "winlink.org") == 0) - { - if (CheckifLocalRMSUser(Addr)) - { - // Local RMS - Leave Here - - Via = 0; // Drop Through - goto PktMsg; - } - else - { - ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); - continue; - } - } - - ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, Addr, Via); - continue; - } - - _strupr(Addr); - - Via = strlop(Addr, '@'); - - if (Via && _stricmp(Via, "winlink.org") == 0) - { - if (CheckifLocalRMSUser(Addr)) - { - // Local RMS - Leave Here - - Via = 0; // Drop Through - } - else - { - ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); - - // Add to B2 Message for RMS - - continue; - } - } - - PktMsg: - - conn->LocalMsg = FALSE; - - // Normal BBS Message - - if (_stricmp(Addr, "SYSOP") == 0) - conn->LocalMsg = TRUE; - else - { - struct UserInfo * ToUser = LookupCall(Addr); - - if (ToUser) - conn->LocalMsg = TRUE; - } - - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 10); - memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); - - strcpy(Msg->to, Addr); - - if (Via) - { - Msg->bid[0] = 0; // if we are forwarding it, we must change BID to be safe - strcpy(Msg->via, Via); - } - - CreateMessageFromBuffer(conn); - } - - if (ToLen) - { - char * B2Hddr = zalloc(ToLen + 1000); - int B2HddrLen; - char DateString[80]; - struct tm * tm; - time_t Date = time(NULL); - char Type[16] = "Private"; - - // Get Type - - if (conn->TempMsg->type == 'B') - strcpy(Type, "Bulletin"); - else if (conn->TempMsg->type == 'T') - strcpy(Type, "Traffic"); - - tm = gmtime(&Date); - - sprintf(DateString, "%04d/%02d/%02d %02d:%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 1000 + ToLen); - - Msg->B2Flags = B2Msg; - - B2HddrLen = sprintf(B2Hddr, - "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n", - SaveMsg->bid, DateString, Type, - SaveMsg->from, ToString, SaveMsg->title, BBSName, SaveMsgLen); - - memcpy(conn->MailBuffer, B2Hddr, B2HddrLen); - memcpy(&conn->MailBuffer[B2HddrLen], SaveBody, SaveMsgLen); - - Msg->length += B2HddrLen; - - strcpy(Msg->to, "RMS"); - - CreateMessageFromBuffer(conn); - - free(B2Hddr); - } - - free(SaveMsg); - free(SaveBody); - conn->MailBuffer = NULL; - conn->MailBufferSize=0; - - if (!(conn->BBSFlags & BBS)) - SendPrompt(conn, conn->UserPointer); - else - if (!(conn->BBSFlags & FBBForwarding)) - { - if (conn->BBSFlags & OUTWARDCONNECT) - BBSputs(conn, "F>\r"); // if Outward connect must be reverse forward - else - BBSputs(conn, ">\r"); - } - - /* - // From a client - Create one copy with all RMS recipients, and another for each packet recipient - - // Merge all RMS To: lines - - ToLen = 0; - ToString[0] = 0; - - for (i = 0; i < Recipients; i++) - { - if (LocalMsg[i]) - continue; // For a local RMS user - - if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || - _stricmp(RecpTo[i], "RMS") == 0) - { - ToLen += strlen(HddrTo[i]); - strcat(ToString, HddrTo[i]); - } - } - - if (ToLen) - { - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 1000); - memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); - - - memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); - memcpy(&conn->MailBuffer[B2To], ToString, ToLen); - - conn->TempMsg->length += ToLen; - - strcpy(Msg->to, "RMS"); - strcpy(Msg->via, "winlink.org"); - - // Must Change the BID - - Msg->bid[0] = 0; - - CreateMessageFromBuffer(conn); - } - - } - - free(ToString); - - for (i = 0; i < Recipients; i++) - { - // Only Process Non - RMS Dests or local RMS Users - - if (LocalMsg[i] == 0) - if (_stricmp (Via[i], "WINLINK.ORG") == 0 || - _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || - _stricmp(RecpTo[i], "RMS") == 0) - continue; - - conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); - memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); - - conn->MailBuffer = malloc(SaveMsgLen + 1000); - memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); - - // Add our To: - - ToLen = strlen(HddrTo[i]); - - if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: - memcpy(HddrTo[i], "To", 2); - - memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); - memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); - - conn->TempMsg->length += ToLen; - - strcpy(Msg->to, RecpTo[i]); - strcpy(Msg->via, Via[i]); - - Msg->bid[0] = 0; - - CreateMessageFromBuffer(conn); - } - } // End not from RMS - - free(SaveMsg); - free(SaveBody); - conn->MailBuffer = NULL; - conn->MailBufferSize=0; - - SetupNextFBBMessage(conn); - return; - - } My__except_Routine("Process Multiple Destinations"); - - BBSputs(conn, "*** Program Error Processing Multiple Destinations\r"); - Flush(conn); - conn->CloseAfterFlush = 20; // 2 Secs - - return; -*/ - - conn->ToCount = 0; - - return; - } - - - CreateMessageFromBuffer(conn); - return; - - } - - Buffer[msglen++] = 0x0a; - - if ((conn->TempMsg->length + msglen) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - nodeprintf(conn, "Failed to extend Message Buffer\r"); - - conn->Flags &= ~GETTINGMESSAGE; - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, msglen); - - conn->TempMsg->length += msglen; -} - -VOID CreateMessageFromBuffer(CIRCUIT * conn) -{ - struct MsgInfo * Msg; - BIDRec * BIDRec; - char * ptr1, * ptr2 = NULL; - char * ptr3, * ptr4; - int FWDCount = 0; - char OldMess[] = "\r\n\r\nOriginal Message:\r\n\r\n"; - time_t Age; - int OurCount; - char * HoldReason = "User has Hold Messages flag set"; - struct UserInfo * user; - - -#ifndef LINBPQ - struct _EXCEPTION_POINTERS exinfo; -#endif - - // If doing SC, Append Old Message - - if (conn->CopyBuffer) - { - if ((conn->TempMsg->length + (int) strlen(conn->CopyBuffer) + 80 )> conn->MailBufferSize) - { - conn->MailBufferSize += (int)strlen(conn->CopyBuffer) + 80; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - nodeprintf(conn, "Failed to extend Message Buffer\r"); - - conn->Flags &= ~GETTINGMESSAGE; - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], OldMess, strlen(OldMess)); - - conn->TempMsg->length += (int)strlen(OldMess); - - memcpy(&conn->MailBuffer[conn->TempMsg->length], conn->CopyBuffer, strlen(conn->CopyBuffer)); - - conn->TempMsg->length += (int)strlen(conn->CopyBuffer); - - free(conn->CopyBuffer); - conn->CopyBuffer = NULL; - } - - // Allocate a message Record slot - - Msg = AllocateMsgRecord(); - memcpy(Msg, conn->TempMsg, sizeof(struct MsgInfo)); - - free(conn->TempMsg); - - // Set number here so they remain in sequence - - GetSemaphore(&MsgNoSemaphore, 0); - Msg->number = ++LatestMsg; - FreeSemaphore(&MsgNoSemaphore); - MsgnotoMsg[Msg->number] = Msg; - - if (Msg->status == 0) - Msg->status = 'N'; - - // Create BID if non supplied - - if (Msg->bid[0] == 0) - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - // if message body had R: lines, get date created from last (not very accurate, but best we can do) - - // Also check if we have had message before to detect loops - - ptr1 = conn->MailBuffer; - OurCount = 0; - - // If it is a B2 Message, Must Skip B2 Header - - if (Msg->B2Flags & B2Msg) - { - ptr1 = strstr(ptr1, "\r\n\r\n"); - if (ptr1) - ptr1 += 4; - else - ptr1 = conn->MailBuffer; - } - -nextline: - - if (memcmp(ptr1, "R:", 2) == 0) - { - // Is if ours? - - // BPQ RLINE Format R:090920/1041Z 6542@N4JOA.#WPBFL.FL.USA.NOAM BPQ1.0.2 - - ptr3 = strchr(ptr1, '@'); - ptr4 = strchr(ptr1, '.'); - - if (ptr3 && ptr4 && (ptr4 > ptr3)) - { - if (memcmp(ptr3+1, BBSName, ptr4-ptr3-1) == 0) - OurCount++; - } - - GetWPBBSInfo(ptr1); // Create WP /I record from R: Line - - // see if another - - ptr2 = ptr1; // save - ptr1 = strchr(ptr1, '\r'); - if (ptr1 == 0) - { - Debugprintf("Corrupt Message %s from %s - truncated within R: line", Msg->bid, Msg->from); - return; - } - ptr1++; - if (*ptr1 == '\n') ptr1++; - - goto nextline; - } - - // ptr2 points to last R: line (if any) - - if (ptr2) - { - struct tm rtime; - time_t result; - - memset(&rtime, 0, sizeof(struct tm)); - - if (ptr2[10] == '/') - { - // Dodgy 4 char year - - sscanf(&ptr2[2], "%04d%02d%02d/%02d%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); - rtime.tm_year -= 1900; - rtime.tm_mon--; - } - else if (ptr2[8] == '/') - { - sscanf(&ptr2[2], "%02d%02d%02d/%02d%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); - - if (rtime.tm_year < 90) - rtime.tm_year += 100; // Range 1990-2089 - rtime.tm_mon--; - } - - // Otherwise leave date as zero, which should be rejected - - // result = _mkgmtime(&rtime); - - if ((result = mktime(&rtime)) != (time_t)-1 ) - { - result -= (time_t)_MYTIMEZONE; - - Msg->datecreated = result; - Age = (time(NULL) - result)/86400; - - if ( Age < -7) - { - Msg->status = 'H'; - HoldReason = "Suspect Date Sent"; - } - else if (Age > BidLifetime || Age > MaxAge) - { - Msg->status = 'H'; - HoldReason = "Message too old"; - - } - else - GetWPInfoFromRLine(Msg->from, ptr2, result); - } - else - { - // Can't decode R: Datestamp - - Msg->status = 'H'; - HoldReason = "Corrupt R: Line - can't determine age"; - } - - if (OurCount > 1) - { - // Message is looping - - Msg->status = 'H'; - HoldReason = "Message may be looping"; - - } - } - - if (strcmp(Msg->to, "WP") == 0) - { - // If Reject WP Bulls is set, Kill message here. - // It should only get here if B2 - otherwise it should be - // rejected earlier - - if (Msg->type == 'B' && FilterWPBulls) - Msg->status = 'K'; - - } - - conn->MailBuffer[Msg->length] = 0; - - if (CheckBadWords(Msg->title) || CheckBadWords(conn->MailBuffer)) - { - Msg->status = 'H'; - HoldReason = "Bad word in title or body"; - } - - if (CheckHoldFilters(Msg->from, Msg->to, Msg->via, Msg->bid)) - { - Msg->status = 'H'; - HoldReason = "Matched Hold Filters"; - } - - if (CheckValidCall(Msg->from) == 0) - { - Msg->status = 'H'; - HoldReason = "Probable Invalid From Call"; - } - - // Process any WP Messages - - if (strcmp(Msg->to, "WP") == 0) - { - if (Msg->status == 'N') - { - ProcessWPMsg(conn->MailBuffer, Msg->length, ptr2); - - if (Msg->type == 'P') // Kill any processed private WP messages. - { - char VIA[80]; - - strcpy(VIA, Msg->via); - strlop(VIA, '.'); - - if (strcmp(VIA, BBSName) == 0) - Msg->status = 'K'; - } - } - } - - CreateMessageFile(conn, Msg); - - BIDRec = AllocateBIDRecord(); - - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - if (Msg->length > MaxTXSize) - { - Msg->status = 'H'; - HoldReason = "Message too long"; - - if (!(conn->BBSFlags & BBS)) - nodeprintf(conn, "*** Warning Message length exceeds sysop-defined maximum of %d - Message will be held\r", MaxTXSize); - } - - // Check for message to internal server - - if (Msg->via[0] == 0 - || _stricmp(Msg->via, BBSName) == 0 // our BBS a - || _stricmp(Msg->via, AMPRDomain) == 0) // our AMPR Address - { - if (CheckforMessagetoServer(Msg)) - { - // Flag as killed and send prompt - - FlagAsKilled(Msg, TRUE); - - if (!(conn->BBSFlags & BBS)) - { - nodeprintf(conn, "Message %d to Server Processed and Killed.\r", Msg->number); - SendPrompt(conn, conn->UserPointer); - } - return; // no need to process further - } - } - - if (Msg->to[0]) - FWDCount = MatchMessagetoBBSList(Msg, conn); - else - { - // If addressed @winlink.org, and to a local user, Keep here. - - char * Call; - char * AT; - - // smtp or rms - don't warn no route - - FWDCount = 1; - - Call = _strupr(_strdup(Msg->via)); - AT = strlop(Call, '@'); - - if (AT && _stricmp(AT, "WINLINK.ORG") == 0) - { - struct UserInfo * user = LookupCall(Call); - - if (user) - { - if (user->flags & F_POLLRMS) - { - Logprintf(LOG_BBS, conn, '?', "SMTP Message @ winlink.org, but local RMS user - leave here"); - strcpy(Msg->to, Call); - strcpy(Msg->via, AT); - if (user->flags & F_BBS) // User is a BBS, so set FWD bit so he can get it - set_fwd_bit(Msg->fbbs, user->BBSNumber); - - } - } - } - free(Call); - } - - // Warn SYSOP if P or T forwarded in, and has nowhere to go - - if ((conn->BBSFlags & BBS) && Msg->type != 'B' && FWDCount == 0 && WarnNoRoute && - strcmp(Msg->to, "SYSOP") && strcmp(Msg->to, "WP")) - { - if (Msg->via[0]) - { - if (_stricmp(Msg->via, BBSName)) // Not for our BBS a - if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address - SendWarningToSYSOP(Msg); - } - else - { - // No via - is it for a local user? - - if (LookupCall(Msg->to) == 0) - SendWarningToSYSOP(Msg); - } - } - - if ((conn->BBSFlags & SYNCMODE) == 0) - { - if (!(conn->BBSFlags & BBS)) - { - nodeprintf(conn, "Message: %d Bid: %s Size: %d\r", Msg->number, Msg->bid, Msg->length); - - if (Msg->via[0]) - { - if (_stricmp(Msg->via, BBSName)) // Not for our BBS a - if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address - - if (FWDCount == 0 && Msg->to[0] != 0) // unless smtp msg - nodeprintf(conn, "@BBS specified, but no forwarding info is available - msg may not be delivered\r"); - } - else - { - if (FWDCount == 0 && conn->LocalMsg == 0 && Msg->type != 'B') - // Not Local and no forward route - nodeprintf(conn, "Message is not for a local user, and no forwarding info is available - msg may not be delivered\r"); - } - if (conn->ToCount == 0) - SendPrompt(conn, conn->UserPointer); - } - else - { - if (!(conn->BBSFlags & FBBForwarding)) - { - if (conn->ToCount == 0) - if (conn->BBSFlags & OUTWARDCONNECT) - nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward - else - nodeprintf(conn, ">\r"); - } - } - } - - if(Msg->to[0] == 0) - SMTPMsgCreated=TRUE; - - if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) - Msg->status = '$'; // Has forwarding - - if (Msg->status == 'H') - { - int Length=0; - char * MailBuffer = malloc(100); - char Title[100]; - - Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); - sprintf(Title, "Message %d Held - %s", Msg->number, HoldReason); - SendMessageToSYSOP(Title, MailBuffer, Length); - } - - BuildNNTPList(Msg); // Build NNTP Groups list - - SaveMessageDatabase(); - SaveBIDDatabase(); - - // If Event Notifications enabled report a new message event - - if (reportNewMesageEvents) - { - char msg[200]; - - //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject - - struct tm *tm = gmtime((time_t *)&Msg->datecreated); - - sprintf_s(msg, sizeof(msg),"%-6d %c %6d %-13s %-6s %02d%02d%02d %s\r", - Msg->number, Msg->type, Msg->length, Msg->to, - Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); - -#ifdef WIN32 - if (pRunEventProgram) - pRunEventProgram("MailNewMsg.exe", msg); -#else - { - char prog[256]; - sprintf(prog, "%s/%s", BPQDirectory, "MailNewMsg"); - RunEventProgram(prog, msg); - } -#endif - } - - - if (EnableUI) -#ifdef LINBPQ - SendMsgUI(Msg); -#else - __try - { - SendMsgUI(Msg); - } - My__except_Routine("SendMsgUI"); -#endif - - user = LookupCall(Msg->to); - - if (user && (user->flags & F_APRSMFOR)) - { - char APRS[128]; - char Call[16]; - int SSID = user->flags >> 28; - - if (SSID) - sprintf(Call, "%s-%d", Msg->to, SSID); - else - strcpy(Call, Msg->to); - - sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); - APISendAPRSMessage(APRS, Call); - } - - return; -} - -VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg) -{ - char MsgFile[MAX_PATH]; - FILE * hFile; - size_t WriteLen=0; - char Mess[255]; - int len; - BOOL AutoImport = FALSE; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - // If title is "Batched messages for AutoImport from BBS xxxxxx and first line is S? and it is - // for this BBS, Import file and set message as Killed. May need to strip B2 Header and R: lines - - if (strcmp(Msg->to, BBSName) == 0 && strstr(Msg->title, "Batched messages for AutoImport from BBS ")) - { - UCHAR * ptr = conn->MailBuffer; - - // If it is a B2 Message, Must Skip B2 Header - - if (Msg->B2Flags & B2Msg) - { - ptr = strstr(ptr, "\r\n\r\n"); - if (ptr) - ptr += 4; - else - ptr = conn->MailBuffer; - } - - if (memcmp(ptr, "R:", 2) == 0) - { - ptr = strstr(ptr, "\r\n\r\n"); //And remove R: Lines - if (ptr) - ptr += 4; - } - - if (*(ptr) == 'S' && ptr[2] == ' ') - { - int HeaderLen = (int)(ptr - conn->MailBuffer); - Msg->length -= HeaderLen; - memmove(conn->MailBuffer, ptr, Msg->length); - Msg->status = 'K'; - AutoImport = TRUE; - } - } - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - WriteLen = fwrite(conn->MailBuffer, 1, Msg->length, hFile); - fclose(hFile); - } - - if (AutoImport) - ImportMessages(NULL, MsgFile, TRUE); - - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - - if (WriteLen != Msg->length) - { - len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); - QueueMsg(conn, Mess, len); - Debugprintf(Mess); - } - return; -} - - - - -VOID SendUnbuffered(int stream, char * msg, int len) -{ -#ifndef LINBPQ - if (stream < 0) - WritetoConsoleWindow(stream, msg, len); - else -#endif - SendMsg(stream, msg, len); -} - -BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen); - -BOOL FindMessagestoForward (CIRCUIT * conn) -{ - struct UserInfo * user = conn->UserPointer; - -#ifndef LINBPQ - - struct _EXCEPTION_POINTERS exinfo; - - __try { -#endif - - // This is a hack so Winpack or WLE users can use forwarding - // protocols to get their messages without being defined as a BBS - - // !!IMPORTANT Getting this wrong can see message repeatedly proposed !! - // !! Anything sent using this must be killed if sent or rejected. - - // I'm not sure about this. I think I only need the PacLinkCalls - // if from RMS Express, and it always sends an FW; line - // Ah, no. What about WinPack ?? - // If from RMS Express must have Temp_B2 or BBS set or SID will - // be rejected. So maybe just Temp_B2 && BBS = 0?? - // No, someone may have Temp_B2 set and not be using WLE ?? So what ?? - - if ((user->flags & F_Temp_B2_BBS) && ((user->flags & F_BBS) == 0) || conn->RMSExpress || conn->PAT) - { - if (conn->PacLinkCalls == NULL) - { - // create a list with just the user call - - char * ptr1; - - conn->PacLinkCalls = zalloc(30); - - ptr1 = (char *)conn->PacLinkCalls; - ptr1 += 16; // Must be room for a null pointer on end (64 bit bug) - strcpy(ptr1, user->Call); - - conn->PacLinkCalls[0] = ptr1; - } - } - - if (conn->SendT && FindMessagestoForwardLoop(conn, 'T', conn->MaxTLen)) - { - conn->LastForwardType = 'T'; - return TRUE; - } - - if (conn->LastForwardType == 'T') - conn->NextMessagetoForward = FirstMessageIndextoForward; - - if (conn->SendP && FindMessagestoForwardLoop(conn, 'P', conn->MaxPLen)) - { - conn->LastForwardType = 'P'; - return TRUE; - } - - if (conn->LastForwardType == 'P') - conn->NextMessagetoForward = FirstMessageIndextoForward; - - if (conn->SendB && FindMessagestoForwardLoop(conn, 'B', conn->MaxBLen)) - { - conn->LastForwardType = 'B'; - return TRUE; - } - - conn->LastForwardType = 0; - return FALSE; -#ifndef LINBPQ - } My__except_Routine("FindMessagestoForward"); -#endif - return FALSE; - -} - - -BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen) -{ - // See if any messages are queued for this BBS - - int m; - struct MsgInfo * Msg; - struct UserInfo * user = conn->UserPointer; - struct FBBHeaderLine * FBBHeader; - BOOL Found = FALSE; - char RLine[100]; - int TotalSize = 0; - time_t NOW = time(NULL); - -// Debugprintf("FMTF entered Call %s Type %c Maxlen %d NextMsg = %d BBSNo = %d", -// conn->Callsign, Type, MaxLen, conn->NextMessagetoForward, user->BBSNumber); - - if (conn->PacLinkCalls || (conn->UserPointer->flags & F_NTSMPS)) // Looking for all messages, so reset - conn->NextMessagetoForward = 1; - - conn->FBBIndex = 0; - - for (m = conn->NextMessagetoForward; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - // If an NTS MPS, see if anything matches - - if (Type == 'T' && (conn->UserPointer->flags & F_NTSMPS)) - { - struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; - int depth; - - if (Msg->type == 'T' && Msg->status == 'N' && Msg->length <= MaxLen && ForwardingInfo) - { - depth = CheckBBSToForNTS(Msg, ForwardingInfo); - - if (depth > -1 && Msg->Locked == 0) - goto Forwardit; - - depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); - - if (depth && Msg->Locked == 0) - goto Forwardit; - - depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); - - if (depth > -1 && Msg->Locked == 0) - goto Forwardit; - } - } - - // If forwarding to Paclink or RMS Express, look for any message matching the - // requested call list with status 'N' (maybe should also be 'P' ??) - - if (conn->PacLinkCalls) - { - int index = 1; - - char * Call = conn->PacLinkCalls[0]; - - while (Call) - { - if (Msg->type == Type && Msg->status == 'N') - { -// Debugprintf("Comparing RMS Call %s %s", Msg->to, Call); - if (_stricmp(Msg->to, Call) == 0) - if (Msg->status == 'N' && Msg->type == Type && Msg->length <= MaxLen) - goto Forwardit; - else - Debugprintf("Call Match but Wrong Type/Len %c %d", Msg->type, Msg->length); - } - Call = conn->PacLinkCalls[index++]; - } -// continue; - } - - if (Msg->type == Type && Msg->length <= MaxLen && (Msg->status != 'H') - && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) - { - // Message to be sent - do a consistancy check (State, etc) - - Forwardit: - - if (Msg->Defered > 0) // = response received - { - Msg->Defered--; - Debugprintf("Message %d deferred", Msg->number); - continue; - } - - if ((Msg->from[0] == 0) || (Msg->to[0] == 0)) - { - int Length=0; - char * MailBuffer = malloc(100); - char Title[100]; - - Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); - sprintf(Title, "Message %d Held - %s", Msg->number, "Missing From: or To: field"); - SendMessageToSYSOP(Title, MailBuffer, Length); - - Msg->status = 'H'; - continue; - } - - conn->NextMessagetoForward = m + 1; // So we don't offer again if defered - - Msg->Locked = 1; // So other MPS can't pick it up - - // if FBB forwarding add to list, eise save pointer - - if (conn->BBSFlags & FBBForwarding) - { - struct tm *tm; - time_t temp; - - FBBHeader = &conn->FBBHeaders[conn->FBBIndex++]; - - FBBHeader->FwdMsg = Msg; - FBBHeader->MsgType = Msg->type; - FBBHeader->Size = Msg->length; - TotalSize += Msg->length; - strcpy(FBBHeader->From, Msg->from); - strcpy(FBBHeader->To, Msg->to); - strcpy(FBBHeader->ATBBS, Msg->via); - strcpy(FBBHeader->BID, Msg->bid); - - // Set up R:Line, so se can add its length to the sise - - memcpy(&temp, &Msg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - FBBHeader->Size += sprintf_s(RLine, sizeof(RLine),"R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - Msg->number, BBSName, HRoute, RlineVer); - - // If using B2 forwarding we need the message size and Compressed size for FC proposal - - if (conn->BBSFlags & FBBB2Mode) - { - if (CreateB2Message(conn, FBBHeader, RLine) == FALSE) - { - char * MailBuffer = malloc(100); - char Title[100]; - int Length; - - // Corrupt B2 Message - - Debugprintf("Corrupt B2 Message found - Message %d will be held", Msg->number); - Msg->status = 'H'; - SaveMessageDatabase(); - - conn->FBBIndex--; - TotalSize -= Msg->length; - memset(&conn->FBBHeaders[conn->FBBIndex], 0, sizeof(struct FBBHeaderLine)); - - Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); - sprintf(Title, "Message %d Held - %s", Msg->number, "Corrupt B2 Message"); - SendMessageToSYSOP(Title, MailBuffer, Length); - - continue; - } - } - - if (conn->FBBIndex == 5 || TotalSize > user->ForwardingInfo->MaxFBBBlockSize) - return TRUE; // Got max number or too big - - Found = TRUE; // Remember we have some - } - else - { - conn->FwdMsg = Msg; - return TRUE; - } - } - } - - return Found; -} - -BOOL SeeifMessagestoForward (int BBSNumber, CIRCUIT * conn) -{ - // See if any messages are queued for this BBS - - // if Conn is not NULL, also check Msg Type - - int m; - struct MsgInfo * Msg; - - for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) - { - if (conn) - { - char Type = Msg->type; - - if ((conn->SendB && Type == 'B') || (conn->SendP && Type == 'P') || (conn->SendT && Type == 'T')) - { -// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); - return TRUE; - } - } - else - { -// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); - return TRUE; - } - } - } - - return FALSE; -} - -int CountMessagestoForward (struct UserInfo * user) -{ - // See if any messages are queued for this BBS - - int m, n=0; - struct MsgInfo * Msg; - int BBSNumber = user->BBSNumber; - int FirstMessage = FirstMessageIndextoForward; - - if ((user->flags & F_NTSMPS)) - FirstMessage = 1; - - for (m = FirstMessage; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) - { - n++; - continue; // So we dont count twice in Flag set and NTS MPS - } - - // if an NTS MPS, also check for any matches - - if (Msg->type == 'T' && (user->flags & F_NTSMPS)) - { - struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; - int depth; - - if (Msg->status == 'N' && ForwardingInfo) - { - depth = CheckBBSToForNTS(Msg, ForwardingInfo); - - if (depth > -1 && Msg->Locked == 0) - { - n++; - continue; - } - depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); - - if (depth && Msg->Locked == 0) - { - n++; - continue; - } - - depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); - - if (depth > -1 && Msg->Locked == 0) - { - n++; - continue; - } - } - } - } - - return n; -} - -int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user) -{ - // See if any messages are queued for this BBS - - int m, n=0; - struct MsgInfo * Msg; - int BBSNumber = user->BBSNumber; - int FirstMessage = FirstMessageIndextoForward; - - if ((user->flags & F_NTSMPS)) - FirstMessage = 1; - - for (m = FirstMessage; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) - { - nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); - continue; // So we dont count twice in Flag set and NTS MPS - } - - // if an NTS MPS, also check for any matches - - if (Msg->type == 'T' && (user->flags & F_NTSMPS)) - { - struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; - int depth; - - if (Msg->status == 'N' && ForwardingInfo) - { - depth = CheckBBSToForNTS(Msg, ForwardingInfo); - - if (depth > -1 && Msg->Locked == 0) - { - nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); - continue; - } - depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); - - if (depth && Msg->Locked == 0) - { - nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); - continue; - } - - depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); - - if (depth > -1 && Msg->Locked == 0) - { - nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); - continue; - } - } - } - } - - return n; -} - -VOID SendWarningToSYSOP(struct MsgInfo * Msg) -{ - int Length=0; - char * MailBuffer = malloc(100); - char Title[100]; - - Length += sprintf(MailBuffer, "Warning - Message %d has nowhere to go", Msg->number); - sprintf(Title, "Warning - Message %d has nowhere to go", Msg->number); - SendMessageToSYSOP(Title, MailBuffer, Length); -} - - - -VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length) -{ - struct MsgInfo * Msg = AllocateMsgRecord(); - BIDRec * BIDRec; - - char MsgFile[MAX_PATH]; - FILE * hFile; - size_t WriteLen=0; - - Msg->length = Length; - - GetSemaphore(&MsgNoSemaphore, 0); - Msg->number = ++LatestMsg; - MsgnotoMsg[Msg->number] = Msg; - - FreeSemaphore(&MsgNoSemaphore); - - strcpy(Msg->from, "SYSTEM"); - if (SendSYStoSYSOPCall) - strcpy(Msg->to, SYSOPCall); - else - strcpy(Msg->to, "SYSOP"); - - strcpy(Msg->title, Title); - - Msg->type = 'P'; - Msg->status = 'N'; - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - BIDRec = AllocateBIDRecord(); - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); - fclose(hFile); - } - - MatchMessagetoBBSList(Msg, NULL); - free(MailBuffer); -} - -VOID CheckBBSNumber(int i) -{ - // Make sure number is unique - - int Count = 0; - struct UserInfo * user; - - for (user = BBSChain; user; user = user->BBSNext) - { - if (user->BBSNumber == i) - { - Count++; - - if (Count > 1) - { - // Second with same number - Renumber this one - - user->BBSNumber = FindFreeBBSNumber(); - - if (user->BBSNumber == 0) - user->BBSNumber = NBBBS; // cant really do much else - - Logprintf(LOG_BBS, NULL, '?', "Duplicate BBS Number found. BBS %s Old BBSNumber %d New BBS Number %d", user->Call, i, user->BBSNumber); - - } - } - } -} - - -int FindFreeBBSNumber() -{ - // returns the lowest number not used by any bbs or message. - - struct MsgInfo * Msg; - struct UserInfo * user; - int i, m; - - for (i = 1; i<= NBBBS; i++) - { - for (user = BBSChain; user; user = user->BBSNext) - { - if (user->BBSNumber == i) - goto nexti; // In use - } - - // Not used by BBS - check messages - - for (m = 1; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - if (check_fwd_bit(Msg->fbbs, i)) - goto nexti; // In use - - if (check_fwd_bit(Msg->forw, i)) - goto nexti; // In use - } - - // Not in Use - - return i; - -nexti:; - - } - - return 0; // All used -} - -BOOL SetupNewBBS(struct UserInfo * user) -{ - user->BBSNumber = FindFreeBBSNumber(); - - if (user->BBSNumber == 0) - return FALSE; - - user->BBSNext = BBSChain; - BBSChain = user; - - SortBBSChain(); - - ReinitializeFWDStruct(user); - - return TRUE; -} - -VOID DeleteBBS(struct UserInfo * user) -{ - struct UserInfo * BBSRec, * PrevBBS = NULL; - -#ifndef LINBPQ - RemoveMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, MF_BYCOMMAND); -#endif - for (BBSRec = BBSChain; BBSRec; PrevBBS = BBSRec, BBSRec = BBSRec->BBSNext) - { - if (user == BBSRec) - { - if (PrevBBS == NULL) // First in chain; - { - BBSChain = BBSRec->BBSNext; - break; - } - PrevBBS->BBSNext = BBSRec->BBSNext; - break; - } - } -} - - -VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo); - -VOID SetupForwardingStruct(struct UserInfo * user) -{ - struct BBSForwardingInfo * ForwardingInfo; - - char Key[100] = "BBSForwarding."; - char Temp[100]; - - HKEY hKey=0; - char RegKey[100] = "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\BBSForwarding\\"; - - int m; - struct MsgInfo * Msg; - - ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - - if (UsingingRegConfig == 0) - { - // Config from file - - if (isdigit(user->Call[0]) || user->Call[0] == '_') - strcat(Key, "*"); - - strcat(Key, user->Call); - - group = config_lookup (&cfg, Key); - - if (group == NULL) // No info - return; - else - { - ForwardingInfo->TOCalls = GetMultiStringValue(group, "TOCalls"); - ForwardingInfo->ConnectScript = GetMultiStringValue(group, "ConnectScript"); - ForwardingInfo->ATCalls = GetMultiStringValue(group, "ATCalls"); - ForwardingInfo->Haddresses = GetMultiStringValue(group, "HRoutes"); - ForwardingInfo->HaddressesP = GetMultiStringValue(group, "HRoutesP"); - ForwardingInfo->FWDTimes = GetMultiStringValue(group, "FWDTimes"); - - ForwardingInfo->Enabled = GetIntValue(group, "Enabled"); - ForwardingInfo->ReverseFlag = GetIntValue(group, "RequestReverse"); - ForwardingInfo->AllowBlocked = GetIntValue(group, "AllowBlocked"); - ForwardingInfo->AllowCompressed = GetIntValue(group, "AllowCompressed"); - ForwardingInfo->AllowB1 = GetIntValue(group, "UseB1Protocol"); - ForwardingInfo->AllowB2 = GetIntValue(group, "UseB2Protocol"); - ForwardingInfo->SendCTRLZ = GetIntValue(group, "SendCTRLZ"); - - if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) - ForwardingInfo->AllowCompressed = TRUE; - - if (ForwardingInfo->AllowCompressed) - ForwardingInfo->AllowBlocked = TRUE; - - ForwardingInfo->PersonalOnly = GetIntValue(group, "FWDPersonalsOnly"); - ForwardingInfo->SendNew = GetIntValue(group, "FWDNewImmediately"); - ForwardingInfo->FwdInterval = GetIntValue(group, "FwdInterval"); - ForwardingInfo->RevFwdInterval = GetIntValue(group, "RevFWDInterval"); - ForwardingInfo->MaxFBBBlockSize = GetIntValue(group, "MaxFBBBlock"); - ForwardingInfo->ConTimeout = GetIntValue(group, "ConTimeout"); - - if (ForwardingInfo->MaxFBBBlockSize == 0) - ForwardingInfo->MaxFBBBlockSize = 10000; - - if (ForwardingInfo->FwdInterval == 0) - ForwardingInfo->FwdInterval = 3600; - - if (ForwardingInfo->ConTimeout == 0) - ForwardingInfo->ConTimeout = 120; - - GetStringValue(group, "BBSHA", Temp); - - if (Temp[0]) - ForwardingInfo->BBSHA = _strdup(Temp); - else - ForwardingInfo->BBSHA = _strdup(""); - } - } - else - { -#ifndef LINBPQ - - int retCode,Type,Vallen; - - strcat(RegKey, user->Call); - retCode = RegOpenKeyEx (REGTREE, RegKey, 0, KEY_QUERY_VALUE, &hKey); - - if (retCode != ERROR_SUCCESS) - return; - else - { - ForwardingInfo->ConnectScript = RegGetMultiStringValue(hKey, "Connect Script"); - ForwardingInfo->TOCalls = RegGetMultiStringValue(hKey, "TOCalls"); - ForwardingInfo->ATCalls = RegGetMultiStringValue(hKey, "ATCalls"); - ForwardingInfo->Haddresses = RegGetMultiStringValue(hKey, "HRoutes"); - ForwardingInfo->HaddressesP = RegGetMultiStringValue(hKey, "HRoutesP"); - ForwardingInfo->FWDTimes = RegGetMultiStringValue(hKey, "FWD Times"); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "Enabled", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->Enabled,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "RequestReverse", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->ReverseFlag,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "AllowCompressed", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowCompressed,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "Use B1 Protocol", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB1,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "Use B2 Protocol", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB2,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "SendCTRLZ", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendCTRLZ,(ULONG *)&Vallen); - - if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) - ForwardingInfo->AllowCompressed = TRUE; - - Vallen=4; - retCode += RegQueryValueEx(hKey, "FWD Personals Only", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->PersonalOnly,(ULONG *)&Vallen); - - Vallen=4; - retCode += RegQueryValueEx(hKey, "FWD New Immediately", 0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendNew,(ULONG *)&Vallen); - - Vallen=4; - RegQueryValueEx(hKey,"FWDInterval",0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->FwdInterval,(ULONG *)&Vallen); - - Vallen=4; - RegQueryValueEx(hKey,"RevFWDInterval",0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->RevFwdInterval,(ULONG *)&Vallen); - - RegQueryValueEx(hKey,"MaxFBBBlock",0, - (ULONG *)&Type,(UCHAR *)&ForwardingInfo->MaxFBBBlockSize,(ULONG *)&Vallen); - - if (ForwardingInfo->MaxFBBBlockSize == 0) - ForwardingInfo->MaxFBBBlockSize = 10000; - - if (ForwardingInfo->FwdInterval == 0) - ForwardingInfo->FwdInterval = 3600; - - Vallen=0; - retCode = RegQueryValueEx(hKey,"BBSHA",0 , (ULONG *)&Type,NULL, (ULONG *)&Vallen); - - if (retCode != 0) - { - // No Key - Get from WP?? - - WPRec * ptr = LookupWP(user->Call); - - if (ptr) - { - if (ptr->first_homebbs) - { - ForwardingInfo->BBSHA = _strdup(ptr->first_homebbs); - } - } - } - - if (Vallen) - { - ForwardingInfo->BBSHA = malloc(Vallen); - RegQueryValueEx(hKey, "BBSHA", 0, (ULONG *)&Type, ForwardingInfo->BBSHA,(ULONG *)&Vallen); - } - - RegCloseKey(hKey); - } -#endif - } - - // Convert FWD Times and H Addresses - - if (ForwardingInfo->FWDTimes) - SetupFwdTimes(ForwardingInfo); - - if (ForwardingInfo->Haddresses) - SetupHAddreses(ForwardingInfo); - - if (ForwardingInfo->HaddressesP) - SetupHAddresesP(ForwardingInfo); - - if (ForwardingInfo->BBSHA) - { - if (ForwardingInfo->BBSHA[0]) - SetupHAElements(ForwardingInfo); - else - { - free(ForwardingInfo->BBSHA); - ForwardingInfo->BBSHA = NULL; - } - } - - for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) - { - Msg=MsgHddrPtr[m]; - - // If any forward bits are set, increment count on BBS record. - - if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) - { - if (Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) - { - user->ForwardingInfo->MsgCount++; - } - } - } -} - -VOID * GetMultiStringValue(config_setting_t * group, char * ValueName) -{ - char * ptr1; - char * MultiString = NULL; - const char * ptr; - int Count = 0; - char ** Value; - config_setting_t *setting; - char * Save; - - Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values - Value[0] = NULL; - - setting = config_setting_get_member (group, ValueName); - - if (setting) - { - ptr = config_setting_get_string (setting); - - Save = _strdup(ptr); // DOnt want to change config string - ptr = Save; - - while (ptr && strlen(ptr)) - { - ptr1 = strchr(ptr, '|'); - - if (ptr1) - *(ptr1++) = 0; - - if (strlen(ptr)) // ignore null elements - { - Value = realloc(Value, (Count+2) * sizeof(void *)); - Value[Count++] = _strdup(ptr); - } - ptr = ptr1; - } - free(Save); - } - - Value[Count] = NULL; - return Value; -} - - -VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName) -{ -#ifdef LINBPQ - return NULL; -#else - int retCode,Type,Vallen; - char * MultiString = NULL; - int ptr, len; - int Count = 0; - char ** Value; - - Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values - - Value[0] = NULL; - - Vallen=0; - - - retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); - - if ((retCode != 0) || (Vallen < 3)) // Two nulls means empty multistring - { - free(Value); - return FALSE; - } - - MultiString = malloc(Vallen); - - retCode = RegQueryValueEx(hKey, ValueName, 0, - (ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen); - - ptr=0; - - while (MultiString[ptr]) - { - len=strlen(&MultiString[ptr]); - - Value = realloc(Value, (Count+2) * sizeof(void *)); - Value[Count++] = _strdup(&MultiString[ptr]); - ptr+= (len + 1); - } - - Value[Count] = NULL; - - free(MultiString); - - return Value; -#endif -} - -VOID FreeForwardingStruct(struct UserInfo * user) -{ - struct BBSForwardingInfo * ForwardingInfo; - int i; - - - ForwardingInfo = user->ForwardingInfo; - - FreeList(ForwardingInfo->TOCalls); - FreeList(ForwardingInfo->ATCalls); - FreeList(ForwardingInfo->Haddresses); - FreeList(ForwardingInfo->HaddressesP); - - i=0; - if(ForwardingInfo->HADDRS) - { - while(ForwardingInfo->HADDRS[i]) - { - FreeList(ForwardingInfo->HADDRS[i]); - i++; - } - free(ForwardingInfo->HADDRS); - free(ForwardingInfo->HADDROffet); - } - - i=0; - if(ForwardingInfo->HADDRSP) - { - while(ForwardingInfo->HADDRSP[i]) - { - FreeList(ForwardingInfo->HADDRSP[i]); - i++; - } - free(ForwardingInfo->HADDRSP); - } - - FreeList(ForwardingInfo->ConnectScript); - FreeList(ForwardingInfo->FWDTimes); - if (ForwardingInfo->FWDBands) - { - i=0; - while(ForwardingInfo->FWDBands[i]) - { - free(ForwardingInfo->FWDBands[i]); - i++; - } - free(ForwardingInfo->FWDBands); - } - if (ForwardingInfo->BBSHAElements) - { - i=0; - while(ForwardingInfo->BBSHAElements[i]) - { - free(ForwardingInfo->BBSHAElements[i]); - i++; - } - free(ForwardingInfo->BBSHAElements); - } - free(ForwardingInfo->BBSHA); - -} - -VOID FreeList(char ** Hddr) -{ - VOID ** Save; - - if (Hddr) - { - Save = (void **)Hddr; - while(Hddr[0]) - { - free(Hddr[0]); - Hddr++; - } - free(Save); - } -} - - -VOID ReinitializeFWDStruct(struct UserInfo * user) -{ - if (user->ForwardingInfo) - { - FreeForwardingStruct(user); - free(user->ForwardingInfo); - } - - SetupForwardingStruct(user); - -} - -VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo) -{ - char ** Times = ForwardingInfo->FWDTimes; - int Start, End; - int Count = 0; - - ForwardingInfo->FWDBands = zalloc(sizeof(struct FWDBAND)); - - if (Times) - { - while(Times[0]) - { - ForwardingInfo->FWDBands = realloc(ForwardingInfo->FWDBands, (Count+2)* sizeof(struct FWDBAND)); - ForwardingInfo->FWDBands[Count] = zalloc(sizeof(struct FWDBAND)); - - Start = atoi(Times[0]); - End = atoi(&Times[0][5]); - - ForwardingInfo->FWDBands[Count]->FWDStartBand = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; - ForwardingInfo->FWDBands[Count]->FWDEndBand = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; - - Count++; - Times++; - } - ForwardingInfo->FWDBands[Count] = NULL; - } -} -void StartForwarding(int BBSNumber, char ** TempScript) -{ - struct UserInfo * user; - struct BBSForwardingInfo * ForwardingInfo ; - time_t NOW = time(NULL); - - - for (user = BBSChain; user; user = user->BBSNext) - { - // See if any messages are queued for this BBS - - ForwardingInfo = user->ForwardingInfo; - - if ((BBSNumber == 0) || (user->BBSNumber == BBSNumber)) - if (ForwardingInfo) - if (ForwardingInfo->Enabled || BBSNumber) // Menu Command overrides enable - if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) - if (BBSNumber || SeeifMessagestoForward(user->BBSNumber, NULL) || - (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) // Menu Command overrides Reverse - { - user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used - - // See if TempScript requested - - if (user->ForwardingInfo->TempConnectScript) - FreeList(user->ForwardingInfo->TempConnectScript); - - user->ForwardingInfo->TempConnectScript = TempScript; - - if (ConnecttoBBS(user)) - ForwardingInfo->Forwarding = TRUE; - } - } - - return; -} - -size_t fwritex(CIRCUIT * conn, void * _Str, size_t _Size, size_t _Count, FILE * _File) -{ - if (_File) - return fwrite(_Str, _Size, _Count, _File); - - // Appending to MailBuffer - - memcpy(&conn->MailBuffer[conn->InputLen], _Str, _Count); - conn->InputLen += (int)_Count; - - return _Count; -} - - -BOOL ForwardMessagestoFile(CIRCUIT * conn, char * FN) -{ - BOOL AddCRLF = FALSE; - BOOL AutoImport = FALSE; - FILE * Handle = NULL; - char * Context; - BOOL Email = FALSE; - time_t now = time(NULL); - char * param; - - FN = strtok_s(FN, " ,", &Context); - - param = strtok_s(NULL, " ,", &Context); - - if (param) - { - if (_stricmp(param, "ADDCRLF") == 0) - AddCRLF = TRUE; - - if (_stricmp(param, "AutoImport") == 0) - AutoImport = TRUE; - - param = strtok_s(NULL, " ,", &Context); - - if (param) - { - if (_stricmp(param, "ADDCRLF") == 0) - AddCRLF = TRUE; - - if (_stricmp(param, "AutoImport") == 0) - AutoImport = TRUE; - - } - } - // If FN is an email address, write to a temp file, and send via rms or emali gateway - - if (strchr(FN, '@') || _memicmp(FN, "RMS:", 4) == 0) - { - Email = TRUE; - AddCRLF =TRUE; - conn->MailBuffer=malloc(100000); - conn->MailBufferSize=100000; - conn->InputLen = 0; - } - else - { - Handle = fopen(FN, "ab"); - - if (Handle == NULL) - { - int err = GetLastError(); - Logprintf(LOG_BBS, conn, '!', "Failed to open Export File %s", FN); - return FALSE; - } - } - - while (FindMessagestoForward(conn)) - { - struct MsgInfo * Msg; - struct tm * tm; - time_t temp; - char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); - int MsgLen; - char * MsgPtr; - char Line[256]; - int len; - struct UserInfo * user = conn->UserPointer; - int Index = 0; - - Msg = conn->FwdMsg; - - if (Email) - if (conn->InputLen + Msg->length + 500 > conn->MailBufferSize) - break; - - if (Msg->type == 'P') - Index = PMSG; - else if (Msg->type == 'B') - Index = BMSG; - else if (Msg->type == 'T') - Index = TMSG; - - - if (Msg->via[0]) - len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, - Msg->via, Msg->from, Msg->bid); - else - len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); - - fwritex(conn, Line, 1, len, Handle); - - len = sprintf(Line, "%s\r\n", Msg->title); - fwritex(conn, Line, 1, len, Handle); - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r\n"); - conn->FwdMsg->length = (int)strlen(MsgBytes); - } - - MsgPtr = MsgBytes; - MsgLen = conn->FwdMsg->length; - - // If a B2 Message, remove B2 Header - - if (conn->FwdMsg->B2Flags & B2Msg) - { - // Remove all B2 Headers, and all but the first part. - - MsgPtr = strstr(MsgBytes, "Body:"); - - if (MsgPtr) - { - MsgLen = atoi(&MsgPtr[5]); - MsgPtr = strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers - - if (MsgPtr) - MsgPtr +=4; - else - MsgPtr = MsgBytes; - - } - else - MsgPtr = MsgBytes; - } - - memcpy(&temp, &Msg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - conn->FwdMsg->number, BBSName, HRoute, RlineVer); - - fwritex(conn, Line, 1, len, Handle); - - if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header - fwritex(conn, "\r\n", 1, 2, Handle); - - fwritex(conn, MsgPtr, 1, MsgLen, Handle); - - if (MsgPtr[MsgLen - 2] == '\r') - fwritex(conn, "/EX\r\n", 1, 5, Handle); - else - fwritex(conn, "\r\n/EX\r\n", 1, 7, Handle); - - if (AddCRLF) - fwritex(conn, "\r\n", 1, 2, Handle); - - free(MsgBytes); - - user->Total.MsgsSent[Index]++; - user->Total.BytesForwardedOut[Index] += MsgLen; - - Msg->datechanged = now; - - clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - conn->FwdMsg->status = 'F'; // Mark as forwarded - conn->FwdMsg->datechanged=time(NULL); - } - - conn->UserPointer->ForwardingInfo->MsgCount--; - } - - if (Email) - { - struct MsgInfo * Msg; - BIDRec * BIDRec; - - if (conn->InputLen == 0) - { - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - - return TRUE; - } - - // Allocate a message Record slot - - Msg = AllocateMsgRecord(); - - // Set number here so they remain in sequence - - GetSemaphore(&MsgNoSemaphore, 0); - Msg->number = ++LatestMsg; - FreeSemaphore(&MsgNoSemaphore); - MsgnotoMsg[Msg->number] = Msg; - - Msg->type = 'P'; - Msg->status = 'N'; - Msg->datecreated = Msg->datechanged = Msg->datereceived = now; - - strcpy(Msg->from, BBSName); - - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - if (AutoImport) - sprintf(Msg->title, "Batched messages for AutoImport from BBS %s", BBSName); - else - sprintf(Msg->title, "Batched messages from BBS %s", BBSName); - - Msg->length = conn->InputLen; - CreateMessageFile(conn, Msg); - - BIDRec = AllocateBIDRecord(); - - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - if (_memicmp(FN, "SMTP:", 5) == 0) - { - strcpy(Msg->via, &FN[5]); - SMTPMsgCreated=TRUE; - } - else - { - strcpy(Msg->to, "RMS"); - if (_memicmp(FN, "RMS:", 4) == 0) - strcpy(Msg->via, &FN[4]); - else - strcpy(Msg->via, FN); - } - - MatchMessagetoBBSList(Msg, conn); - - SaveMessageDatabase(); - SaveBIDDatabase(); - } - else - fclose(Handle); - - SaveMessageDatabase(); - return TRUE; -} - -BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle) -{ - struct tm * tm; - time_t temp; - - char * MsgBytes = ReadMessageFile(Msg->number); - char * MsgPtr; - char Line[256]; - int len; - int MsgLen = Msg->length; - - if (Msg->via[0]) - len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, - Msg->via, Msg->from, Msg->bid); - else - len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); - - fwrite(Line, 1, len, Handle); - - len = sprintf(Line, "%s\r\n", Msg->title); - fwrite(Line, 1, len, Handle); - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r\n"); - MsgLen = (int)strlen(MsgBytes); - } - - MsgPtr = MsgBytes; - - // If a B2 Message, remove B2 Header - - if (Msg->B2Flags & B2Msg) - { - // Remove all B2 Headers, and all but the first part. - - MsgPtr = strstr(MsgBytes, "Body:"); - - if (MsgPtr) - { - MsgLen = atoi(&MsgPtr[5]); - - MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers - - if (MsgPtr) - MsgPtr +=4; - else - MsgPtr = MsgBytes; - - } - else - MsgPtr = MsgBytes; - } - - memcpy(&temp, &Msg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - Msg->number, BBSName, HRoute, RlineVer); - - fwrite(Line, 1, len, Handle); - - if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header - fwrite("\r\n", 1, 2, Handle); - - fwrite(MsgPtr, 1, MsgLen, Handle); - - if (MsgPtr[MsgLen - 2] == '\r') - fwrite("/EX\r\n", 1, 5, Handle); - else - fwrite("\r\n/EX\r\n", 1, 7, Handle); - - free(MsgBytes); - - return TRUE; - -} - -BOOL ConnecttoBBS (struct UserInfo * user) -{ - int n, p; - CIRCUIT * conn; - struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; - - for (n = NumberofStreams-1; n >= 0 ; n--) - { - conn = &Connections[n]; - - if (conn->Active == FALSE) - { - p = conn->BPQStream; - memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything - conn->BPQStream = p; - - // Can't set Active until Connected or Stuck Session detertor can clear session. - // But must set Active before Connected() runs or will appear is Incoming Connect. - // Connected() is semaphored, so get semaphore before ConnectUsingAppl - // Probably better to semaphore lost session code instead - - - strcpy(conn->Callsign, user->Call); - conn->BBSFlags |= (RunningConnectScript | OUTWARDCONNECT); - conn->UserPointer = user; - - Logprintf(LOG_BBS, conn, '|', "Connecting to BBS %s", user->Call); - - ForwardingInfo->MoreLines = TRUE; - - GetSemaphore(&ConSemaphore, 1); - conn->Active = TRUE; - ConnectUsingAppl(conn->BPQStream, BBSApplMask); - FreeSemaphore(&ConSemaphore); - - // If we are sending to a dump pms we may need to connect using the message sender's callsign. - // But we wont know until we run the connect script, which is a bit late to change call. Could add - // flag to forwarding config, but easier to look for SETCALLTOSENDER in the connect script. - - if (strstr(ForwardingInfo->ConnectScript[0], "SETCALLTOSENDER")) - { - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; - conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; - - if (FindMessagestoForward(conn) && conn->FwdMsg) - { - // We have a message to send - - struct MsgInfo * Msg; - unsigned char AXCall[7]; - - Msg = conn->FwdMsg; - ConvToAX25(Msg->from, AXCall); - ChangeSessionCallsign(p, AXCall); - - conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER | NEWPACCOM; - conn->NextMessagetoForward = 0; // was set by FindMessages - } - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; - } -#ifdef LINBPQ - { - BPQVECSTRUC * SESS; - SESS = &BPQHOSTVECTOR[conn->BPQStream - 1]; - - if (SESS->HOSTSESSION == NULL) - { - Logprintf(LOG_BBS, NULL, '|', "No L4 Sessions for connect to BBS %s", user->Call); - return FALSE; - } - - SESS->HOSTSESSION->Secure_Session = 1; - } -#endif - - strcpy(conn->Callsign, user->Call); - - // Connected Event will trigger connect to remote system - - RefreshMainWindow(); - - return TRUE; - } - } - - Logprintf(LOG_BBS, NULL, '|', "No Free Streams for connect to BBS %s", user->Call); - - return FALSE; - -} - -struct DelayParam -{ - struct UserInfo * User; - CIRCUIT * conn; - int Delay; -}; - -struct DelayParam DParam; // Not 100% safe, but near enough - -VOID ConnectDelayThread(struct DelayParam * DParam) -{ - struct UserInfo * User = DParam->User; - int Delay = DParam->Delay; - - User->ForwardingInfo->Forwarding = TRUE; // Minimize window for two connects - - Sleep(Delay); - - User->ForwardingInfo->Forwarding = TRUE; - ConnecttoBBS(User); - - return; -} - -VOID ConnectPauseThread(struct DelayParam * DParam) -{ - CIRCUIT * conn = DParam->conn; - int Delay = DParam->Delay; - char Msg[] = "Pause Ok\r "; - - Sleep(Delay); - - ProcessBBSConnectScript(conn, Msg, 9); - - return; -} - - -/* -BOOL ProcessBBSConnectScriptInner(CIRCUIT * conn, char * Buffer, int len); - - -BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) -{ - BOOL Ret; - GetSemaphore(&ScriptSEM); - Ret = ProcessBBSConnectScriptInner(conn, Buffer, len); - FreeSemaphore(&ScriptSEM); - - return Ret; -} -*/ - -BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) -{ - struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; - char ** Scripts; - char callsign[10]; - int port, sesstype, paclen, maxframe, l4window; - char * ptr, * ptr2; - - WriteLogLine(conn, '<',Buffer, len-1, LOG_BBS); - - Buffer[len]=0; - _strupr(Buffer); - - if (ForwardingInfo->TempConnectScript) - Scripts = ForwardingInfo->TempConnectScript; - else - Scripts = ForwardingInfo->ConnectScript; - - if (ForwardingInfo->ScriptIndex == -1) - { - // First Entry - if first line is TIMES, check and skip forward if necessary - - int n = 0; - int Start, End; - time_t now = time(NULL), StartSecs, EndSecs; - char * Line; - - if (Localtime) - now -= (time_t)_MYTIMEZONE; - - now %= 86400; - Line = Scripts[n]; - - if (_memicmp(Line, "TIMES", 5) == 0) - { - NextBand: - Start = atoi(&Line[6]); - End = atoi(&Line[11]); - - StartSecs = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; - EndSecs = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; - - if ((StartSecs <= now) && (EndSecs >= now)) - goto InBand; // In band - - // Look for next TIME - NextLine: - Line = Scripts[++n]; - - if (Line == NULL) - { - // No more lines - Disconnect - - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (_memicmp(Line, "TIMES", 5) != 0) - goto NextLine; - else - goto NextBand; -InBand: - ForwardingInfo->ScriptIndex = n; - } - - } - else - { - // Dont check first time through - - if (strcmp(Buffer, "*** CONNECTED ") != 0) - { - if (Scripts[ForwardingInfo->ScriptIndex] == NULL || - _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished - _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished - { - ForwardingInfo->MoreLines = FALSE; - } - if (!ForwardingInfo->MoreLines) - goto CheckForSID; - } - } - - if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || - (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || - strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || - strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || - strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || - strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) - { - // Connect Failed - - char * Cmd = Scripts[++ForwardingInfo->ScriptIndex]; - int Delay = 1000; - - // Look for an alternative connect block (Starting with ELSE) - - ElseLoop: - - // Skip any comments - - while (Cmd && ((strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#'))) - Cmd = Scripts[++ForwardingInfo->ScriptIndex]; - - // TIMES terminates a script - - if (Cmd == 0 || _memicmp(Cmd, "TIMES", 5) == 0) // Only Check until script is finished - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (_memicmp(Cmd, "ELSE", 4) != 0) - { - Cmd = Scripts[++ForwardingInfo->ScriptIndex]; - goto ElseLoop; - } - - if (_memicmp(&Cmd[5], "DELAY", 5) == 0) - Delay = atoi(&Cmd[10]) * 1000; - else - Delay = 1000; - - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - - DParam.Delay = Delay; - DParam.User = conn->UserPointer; - - _beginthread((void (*)(void *))ConnectDelayThread, 0, &DParam); - - return FALSE; - } - - // The pointer is only updated when we get the connect, so we can tell when the last line is acked - // The first entry is always from Connected event, so don't have to worry about testing entry -1 below - - - // NETROM to KA node returns - - //c 1 milsw - //WIRAC:N9PMO-2} Connected to MILSW - //###CONNECTED TO NODE MILSW(N9ZXS) CHANNEL A - //You have reached N9ZXS's KA-Node MILSW - //ENTER COMMAND: B,C,J,N, or Help ? - - //C KB9PRF-7 - //###LINK MADE - //###CONNECTED TO NODE KB9PRF-7(KB9PRF-4) CHANNEL A - - // Look for (Space)Connected so we aren't fooled by ###CONNECTED TO NODE, which is not - // an indication of a connect. - - if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || - strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) - { - // If connected to SYNC, save IP address and port - - char * Cmd; - - if (strstr(Buffer, "*** CONNECTED TO SYNC")) - { - char * IPAddr = &Buffer[22]; - char * Port = strlop(IPAddr, ':'); - - if (Port) - { - if (conn->SyncHost) - free(conn->SyncHost); - - conn->SyncHost = _strdup(IPAddr); - conn->SyncPort = atoi(Port); - } - } - - if (conn->SkipConn) - { - conn->SkipConn = FALSE; - return TRUE; - } - - LoopBack: - - Cmd = Scripts[++ForwardingInfo->ScriptIndex]; - - // Only Check until script is finished - - if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) - goto LoopBack; // Blank line - - if (Cmd && _memicmp(Cmd, "TIMES", 5) != 0 && _memicmp(Cmd, "ELSE", 4) != 0) // Only Check until script is finished - { - if (_memicmp(Cmd, "MSGTYPE", 7) == 0) - { - char * ptr; - - // Select Types to send. Only send types in param. Only reverse if R in param - - _strupr(Cmd); - - Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); - - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; - - strcpy(conn->MSGTYPES, &Cmd[8]); - - if (strchr(&Cmd[8], 'R')) conn->DoReverse = TRUE; - - ptr = strchr(&Cmd[8], 'B'); - - if (ptr) - { - conn->SendB = TRUE; - conn->MaxBLen = atoi(++ptr); - if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; - } - - ptr = strchr(&Cmd[8], 'T'); - - if (ptr) - { - conn->SendT = TRUE; - conn->MaxTLen = atoi(++ptr); - if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; - } - ptr = strchr(&Cmd[8], 'P'); - - if (ptr) - { - conn->SendP = TRUE; - conn->MaxPLen = atoi(++ptr); - if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; - } - - // If nothing to do, terminate script - - if (conn->DoReverse || SeeifMessagestoForward(conn->UserPointer->BBSNumber, conn)) - goto LoopBack; - - Logprintf(LOG_BBS, conn, '?', "Nothing to do - quitting"); - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (_memicmp(Cmd, "INTERLOCK ", 10) == 0) - { - // Used to limit connects on a port to 1 - - int Port; - char Option[80]; - - Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); - - sscanf(&Cmd[10], "%d %s", &Port, &Option[0]); - - if (CountConnectionsOnPort(Port)) - { - Logprintf(LOG_BBS, conn, '?', "Interlocked Port is busy - quitting"); - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - goto LoopBack; - } - - if (_memicmp(Cmd, "RADIO AUTH", 10) == 0) - { - // Generate a Password to enable RADIO commands on a remote node - char AuthCommand[80]; - - _strupr(Cmd); - strcpy(AuthCommand, Cmd); - - CreateOneTimePassword(&AuthCommand[11], &Cmd[11], 0); - - nodeprintf(conn, "%s\r", AuthCommand); - return TRUE; - } - - if (_memicmp(Cmd, "SKIPCON", 7) == 0) - { - // Remote Node sends Connected in CTEXT - we need to swallow it - - conn->SkipConn = TRUE; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "SendWL2KPM", 10) == 0|| _memicmp(Cmd, "SendWL2KFW", 10) == 0) - { - // Send ;FW: command - - conn->SendWL2KFW = TRUE; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "SKIPPROMPT", 10) == 0) - { - // Remote Node sends > at end of CTEXT - we need to swallow it - - conn->SkipPrompt++; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "TEXTFORWARDING", 10) == 0) - { - conn->BBSFlags |= TEXTFORWARDING; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "SETCALLTOSENDER", 15) == 0) - { - conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "RADIOONLY", 9) == 0) - { - conn->BBSFlags |= WINLINKRO; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "SYNC", 4) == 0) - { - conn->BBSFlags |= SYNCMODE; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "NEEDLF", 6) == 0) - { - conn->BBSFlags |= NEEDLF; - goto CheckForEnd; - } - - if (_memicmp(Cmd, "MCASTRX", 6) == 0) - { - conn->BBSFlags |= MCASTRX; - conn->MCastListenTime = atoi(&Cmd[7]) * 6; // Time to run session for *6 as value is mins put timer ticks 10 secs - - // send MCAST to Node - - nodeprintfEx(conn, "MCAST\r"); - return TRUE; - } - - if (_memicmp(Cmd, "FLARQ", 5) == 0) - { - conn->BBSFlags |= FLARQMAIL; - - CheckForEnd: - if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || - memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished - memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished - ForwardingInfo->MoreLines = FALSE; - - goto LoopBack; - } - if (_memicmp(Cmd, "PAUSE", 5) == 0) - { - // Pause script - - Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); - - DParam.Delay = atoi(&Cmd[6]) * 1000; - DParam.conn = conn; - - _beginthread((void (*)(void *))ConnectPauseThread, 0, &DParam); - - return TRUE; - } - - if (_memicmp(Cmd, "FILE", 4) == 0) - { - if (Cmd[4] == 0) - { - // Missing Filename - - Logprintf(LOG_BBS, conn, '!', "Export file name missing"); - } - else - ForwardMessagestoFile(conn, &Cmd[5]); - - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (_memicmp(Cmd, "SMTP", 4) == 0) - { - conn->NextMessagetoForward = FirstMessageIndextoForward; - conn->UserPointer->Total.ConnectsOut++; - - SendAMPRSMTP(conn); - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - - if (_memicmp(Cmd, "IMPORT", 6) == 0) - { - char * File, * Context; - int Num; - char * Temp = _strdup(Cmd); - - File = strtok_s(&Temp[6], " ", &Context); - - if (File && File[0]) - { - Num = ImportMessages(NULL, File, TRUE); - - Logprintf(LOG_BBS, NULL, '|', "Imported %d Message(s) from %s", Num, File); - - if (Context && _stricmp(Context, "delete") == 0) - DeleteFile(File); - } - free(Temp); - - if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || - memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished - memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - goto LoopBack; - } - - // Anything else is sent to Node - - // Replace \ with # so can send commands starting with # - - if (Cmd[0] == '\\') - { - Cmd[0] = '#'; - nodeprintfEx(conn, "%s\r", Cmd); - Cmd[0] = '\\'; // Put \ back in script - } - else - nodeprintfEx(conn, "%s\r", Cmd); - - return TRUE; - } - - // End of script. - - ForwardingInfo->MoreLines = FALSE; - - if (conn->BBSFlags & MCASTRX) - { - // No session with Multicast, so no SID - - conn->BBSFlags &= ~RunningConnectScript; - return TRUE; - } - - if (conn->BBSFlags & FLARQMAIL) - { - // FLARQ doesnt send a prompt - Just send message(es) - - conn->UserPointer->Total.ConnectsOut++; - conn->BBSFlags &= ~RunningConnectScript; - ForwardingInfo->LastReverseForward = time(NULL); - - // Update Paclen - - GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); - - if (paclen > 0) - conn->paclen = paclen; - - SendARQMail(conn); - return TRUE; - } - - - return TRUE; - } - - ptr = strchr(Buffer, '}'); - - if (ptr && ForwardingInfo->MoreLines) // Beware it could be part of ctext - { - // Could be respsonse to Node Command - - ptr+=2; - - ptr2 = strchr(&ptr[0], ' '); - - if (ptr2) - { - if (_memicmp(ptr, Scripts[ForwardingInfo->ScriptIndex], ptr2-ptr) == 0) // Reply to last sscript command - { - if (Scripts[ForwardingInfo->ScriptIndex+1] && _memicmp(Scripts[ForwardingInfo->ScriptIndex+1], "else", 4) == 0) - { - // stray match or misconfigured - - return TRUE; - } - - ForwardingInfo->ScriptIndex++; - - if (Scripts[ForwardingInfo->ScriptIndex]) - if (_memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) != 0) - nodeprintf(conn, "%s\r", Scripts[ForwardingInfo->ScriptIndex]); - - return TRUE; - } - } - } - - // Not Success or Fail. If last line is still outstanding, wait fot Response - // else look for SID or Prompt - - if (conn->SkipPrompt && Buffer[len-2] == '>') - { - conn->SkipPrompt--; - return TRUE; - } - - if (ForwardingInfo->MoreLines) - return TRUE; - - // No more steps, Look for SID or Prompt - -CheckForSID: - - if (strstr(Buffer, "POSYNCHELLO")) // RMS RELAY Sync process - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - conn->NextMessagetoForward = FirstMessageIndextoForward; - conn->UserPointer->Total.ConnectsOut++; - ForwardingInfo->LastReverseForward = time(NULL); - - ProcessLine(conn, 0, Buffer, len); - return FALSE; - } - - if (strstr(Buffer, "SORRY, NO")) // URONODE - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - if (memcmp(Buffer, ";PQ: ", 5) == 0) - { - // Secure CMS challenge - - int Len; - struct UserInfo * User = conn->UserPointer; - char * Pass = User->CMSPass; - int Response ; - char RespString[12]; - char ConnectingCall[10]; - -#ifdef LINBPQ - BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; -#else - BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; -#endif - - SESS += conn->BPQStream - 1; - - ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); - - strlop(ConnectingCall, ' '); - - if (Pass[0] == 0) - { - Pass = User->pass; // Old Way - if (Pass[0] == 0) - { - strlop(ConnectingCall, '-'); - User = LookupCall(ConnectingCall); - if (User) - Pass = User->CMSPass; - } - } - - // - - Response = GetCMSHash(&Buffer[5], Pass); - - sprintf(RespString, "%010d", Response); - - Len = sprintf(conn->SecureMsg, ";PR: %s\r", &RespString[2]); - - // Save challengs in case needed for FW lines - - strcpy(conn->PQChallenge, &Buffer[5]); - - return FALSE; - } - - - if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID - { - // Update PACLEN - - GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); - - if (paclen > 0) - conn->paclen = paclen; - - - Parse_SID(conn, &Buffer[1], len-4); - - if (conn->BBSFlags & FBBForwarding) - { - conn->FBBIndex = 0; // ready for first block; - memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); - conn->FBBChecksum = 0; - } - - return TRUE; - } - - if (memcmp(Buffer, "[PAKET ", 7) == 0) - { - conn->BBSFlags |= BBS; - conn->BBSFlags |= MBLFORWARDING; - } - - if (Buffer[len-2] == '>') - { - if (conn->SkipPrompt) - { - conn->SkipPrompt--; - return TRUE; - } - - conn->NextMessagetoForward = FirstMessageIndextoForward; - conn->UserPointer->Total.ConnectsOut++; - conn->BBSFlags &= ~RunningConnectScript; - ForwardingInfo->LastReverseForward = time(NULL); - - if (memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING)) - { - // PK232. Don't send a SID, and switch to Text Mode - - conn->BBSFlags |= (BBS | TEXTFORWARDING); - conn->Flags |= SENDTITLE; - - // Send Message. There is no mechanism for reverse forwarding - - if (FindMessagestoForward(conn) && conn->FwdMsg) - { - struct MsgInfo * Msg; - - // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ - - Msg = conn->FwdMsg; - - if ((conn->BBSFlags & SETCALLTOSENDER)) - nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, - (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); - else - nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, - (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, - Msg->from, Msg->bid); - } - else - { - conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered - Disconnect(conn->BPQStream); - return FALSE; - } - - return TRUE; - } - - if (strcmp(conn->Callsign, "RMS") == 0 || conn->SendWL2KFW) - { - // Build a ;FW: line with all calls with PollRMS Set - - // According to Lee if you use secure login the first - // must be the BBS call - // Actually I don't think we need the first, - // as that is implied - - // If a secure password is available send the new - // call|response format. - - // I think this should use the session callsign, which - // normally will be the BBS ApplCall, and not the BBS Name, - // but coudl be changed by *** LINKED - - int i, s; - char FWLine[10000] = ";FW: "; - struct UserInfo * user; - char RMSCall[20]; - char ConnectingCall[10]; - -#ifdef LINBPQ - BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; -#else - BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; -#endif - - SESS += conn->BPQStream - 1; - - ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); - strlop(ConnectingCall, ' '); - - strcat (FWLine, ConnectingCall); - - for (i = 0; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - - if (user->flags & F_POLLRMS) - { - if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1; - - for (s = 0; s < 16; s++) - { - if (user->RMSSSIDBits & (1 << s)) - { - if (s) - sprintf(RMSCall, "%s-%d", user->Call, s); - else - sprintf(RMSCall, "%s", user->Call); - - // We added connectingcall at front - - if (strcmp(RMSCall, ConnectingCall) != 0) - { - strcat(FWLine, " "); - strcat(FWLine, RMSCall); - - if (user->CMSPass[0]) - { - int Response = GetCMSHash(conn->PQChallenge, user->CMSPass); - char RespString[12]; - - sprintf(RespString, "%010d", Response); - strcat(FWLine, "|"); - strcat(FWLine, &RespString[2]); - } - } - } - } - } - } - - strcat(FWLine, "\r"); - - nodeprintf(conn, FWLine); - } - - // Only declare B1 and B2 if other end did, and we are configued for it - - nodeprintfEx(conn, BBSSID, "BPQ-", - Ver[0], Ver[1], Ver[2], Ver[3], - (conn->BBSFlags & FBBCompressed) ? "B" : "", - (conn->BBSFlags & FBBB1Mode && !(conn->BBSFlags & FBBB2Mode)) ? "1" : "", - (conn->BBSFlags & FBBB2Mode) ? "2" : "", - (conn->BBSFlags & FBBForwarding) ? "F" : "", - (conn->BBSFlags & WINLINKRO) ? "" : "J"); - - if (conn->SecureMsg[0]) - { - struct UserInfo * user; - BBSputs(conn, conn->SecureMsg); - conn->SecureMsg[0] = 0; - - // Also send a Location Comment Line - - //; GM8BPQ-10 DE G8BPQ (IO92KX) - //; WL2K DE GM8BPQ () (PAT) - - user = LookupCall(BBSName); - - if (LOC && LOC[0]) - nodeprintf(conn, "; WL2K DE %s (%s)\r", BBSName, LOC); - } - - if (conn->BPQBBS && conn->MSGTYPES[0]) - - // Send a ; MSGTYPES to control what he sends us - - nodeprintf(conn, "; MSGTYPES %s\r", conn->MSGTYPES); - - if (conn->BBSFlags & FBBForwarding) - { - if (!FBBDoForward(conn)) // Send proposal if anthing to forward - { - if (conn->DoReverse) - FBBputs(conn, "FF\r"); - else - { - FBBputs(conn, "FQ\r"); - conn->CloseAfterFlush = 20; // 2 Secs - } - } - - return TRUE; - } - - return TRUE; - } - - return TRUE; -} - -VOID Parse_SID(CIRCUIT * conn, char * SID, int len) -{ - ChangeSessionIdletime(conn->BPQStream, BBSIDLETIME); // Default Idletime for BBS Sessions - - // scan backwards for first '-' - - if (strstr(SID, "BPQCHATSERVER")) - { - Disconnect(conn->BPQStream); - return; - } - - if (strstr(SID, "RMS Ex") || strstr(SID, "Winlink Ex")) - { - conn->RMSExpress = TRUE; - conn->Paclink = FALSE; - conn->PAT = FALSE; - - // Set new RMS Users as RMS User - - if (conn->NewUser) - conn->UserPointer->flags |= F_Temp_B2_BBS; - } - - if (stristr(SID, "PAT")) - { - // Set new PAT Users as RMS User - - conn->RMSExpress = FALSE; - conn->Paclink = FALSE; - conn->PAT = TRUE; - - if (conn->NewUser) - conn->UserPointer->flags |= F_Temp_B2_BBS; - } - if (strstr(SID, "Paclink")) - { - conn->RMSExpress = FALSE; - conn->Paclink = TRUE; - } - - if (strstr(SID, "WL2K-")) - { - conn->WL2K = TRUE; - conn->BBSFlags |= WINLINKRO; - } - - if (strstr(SID, "MFJ-")) - { - conn->BBSFlags |= MFJMODE; - } - - if (_memicmp(SID, "OpenBCM", 7) == 0) - { - // We should really only do this on Telnet Connections, as OpenBCM flag is used to remove relnet transparency - - - conn->OpenBCM = TRUE; - } - - if (_memicmp(SID, "PMS-3.2", 7) == 0) - { - // Paccom TNC that doesn't send newline prompt ater receiving subject - - conn->BBSFlags |= NEWPACCOM; - } - - // See if BPQ for selective forwarding - - if (strstr(SID, "BPQ")) - conn->BPQBBS = TRUE; - - while (len > 0) - { - switch (SID[len--]) - { - case '-': - - len=0; - break; - - case '$': - - conn->BBSFlags |= BBS | MBLFORWARDING; - conn->Paging = FALSE; - - break; - - case 'F': // FBB Blocked Forwarding - - // We now support blocked uncompressed. Not necessarily compatible with FBB - - if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) - { - // We need to allocate a forwarding structure - - conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; - conn->UserPointer->ForwardingInfo->AllowBlocked = TRUE; - conn->UserPointer->BBSNumber = NBBBS; - } - - if (conn->UserPointer->ForwardingInfo->AllowBlocked) - { - conn->BBSFlags |= FBBForwarding | BBS; - conn->BBSFlags &= ~MBLFORWARDING; - - conn->Paging = FALSE; - - if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) - { - // We need to allocate a forwarding structure - - conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; - conn->UserPointer->BBSNumber = NBBBS; - } - - // Allocate a Header Block - - conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); - } - break; - - case 'J': - - // Suspected to be associated with Winlink Radio Only - - conn->BBSFlags &= ~WINLINKRO; - break; - - case 'B': - - if (conn->UserPointer->ForwardingInfo->AllowCompressed) - { - conn->BBSFlags |= FBBCompressed; - conn->DontSaveRestartData = FALSE; // Allow restarts - - // Look for 1 or 2 or 12 as next 2 chars - - if (SID[len+2] == '1') - { - if (conn->UserPointer->ForwardingInfo->AllowB1 || - conn->UserPointer->ForwardingInfo->AllowB2) // B2 implies B1 - conn->BBSFlags |= FBBB1Mode; - - if (SID[len+3] == '2') - if (conn->UserPointer->ForwardingInfo->AllowB2) - conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) - - break; - } - - if (SID[len+2] == '2') - { - if (conn->UserPointer->ForwardingInfo->AllowB2) - conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) - - if (conn->UserPointer->ForwardingInfo->AllowB1) - conn->BBSFlags |= FBBB1Mode; // B2 should allow fallback to B1 (but RMS doesnt!) - - } - break; - } - - break; - } - } - - // Only allow blocked non-binary to other BPQ Nodes - - if ((conn->BBSFlags & FBBForwarding) && ((conn->BBSFlags & FBBCompressed) == 0) && (conn->BPQBBS == 0)) - { - // Switch back to MBL - - conn->BBSFlags |= MBLFORWARDING; - conn->BBSFlags &= ~FBBForwarding; // Turn off FBB Blocked - } - - return; -} - -VOID BBSSlowTimer() -{ - ConnectionInfo * conn; - int n; - - // Called every 10 seconds - - MCastTimer(); - - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (conn->Active == TRUE) - { - // Check for stuck BBS sessions (BBS session but no Node Session) - - int state; - - GetSemaphore(&ConSemaphore, 1); - SessionStateNoAck(conn->BPQStream, &state); - FreeSemaphore(&ConSemaphore); - - if (state == 0) // No Node Session - { - // is it safe just to clear Active ?? - - conn->InputMode = 0; // So Disconnect wont save partial transfer - conn->BBSFlags = 0; - Disconnected (conn->BPQStream); - continue; - } - - if (conn->BBSFlags & MCASTRX) - MCastConTimer(conn); - - - // Check SIDTImers - used to detect failure to compete SID Handshake - - if (conn->SIDResponseTimer) - { - conn->SIDResponseTimer--; - if (conn->SIDResponseTimer == 0) - { - // Disconnect Session - - Disconnect(conn->BPQStream); - } - } - } - } - - // Flush logs - - for (n = 0; n < 4; n++) - { - if (LogHandle[n]) - { - time_t LT = time(NULL); - if ((LT - LastLogTime[n]) > 30) - { - LastLogTime[n] = LT; - fclose(LogHandle[n]); - LogHandle[n] = NULL; - } - } - } -} - - -VOID FWDTimerProc() -{ - struct UserInfo * user; - struct BBSForwardingInfo * ForwardingInfo ; - time_t NOW = time(NULL); - - for (user = BBSChain; user; user = user->BBSNext) - { - // See if any messages are queued for this BBS - - ForwardingInfo = user->ForwardingInfo; - ForwardingInfo->FwdTimer+=10; - - if (ForwardingInfo->FwdTimer >= ForwardingInfo->FwdInterval) - { - ForwardingInfo->FwdTimer=0; - - if (ForwardingInfo->FWDBands && ForwardingInfo->FWDBands[0]) - { - // Check Timebands - - struct FWDBAND ** Bands = ForwardingInfo->FWDBands; - int Count = 0; - time_t now = time(NULL); - - if (Localtime) - now -= (time_t)_MYTIMEZONE; - - now %= 86400; // Secs in day - - while(Bands[Count]) - { - if ((Bands[Count]->FWDStartBand < now) && (Bands[Count]->FWDEndBand >= now)) - goto FWD; // In band - - Count++; - } - continue; // Out of bands - } - FWD: - if (ForwardingInfo->Enabled) - { - if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) - { - //Temp Debug Code - -// Debugprintf("ReverseFlag = %d, Msgs to Forward Flag %d Msgs to Forward Count %d", -// ForwardingInfo->ReverseFlag, -// SeeifMessagestoForward(user->BBSNumber, NULL), -// CountMessagestoForward(user)); - - if (SeeifMessagestoForward(user->BBSNumber, NULL) || - (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) - - { - user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used - - - // remove any old TempScript - - if (user->ForwardingInfo->TempConnectScript) - { - FreeList(user->ForwardingInfo->TempConnectScript); - user->ForwardingInfo->TempConnectScript = NULL; - } - - if (ConnecttoBBS(user)) - ForwardingInfo->Forwarding = TRUE; - } - } - } - } - } -} - -VOID * _zalloc_dbg(size_t len, int type, char * file, int line) -{ - // ?? malloc and clear - - void * ptr; - -#ifdef WIN32 - ptr=_malloc_dbg(len, type, file, line); -#else - ptr = malloc(len); -#endif - if (ptr) - memset(ptr, 0, len); - - return ptr; -} - -struct MsgInfo * FindMessageByNumber(int msgno) - { - int m=NumberofMessages; - - struct MsgInfo * Msg; - - do - { - Msg=MsgHddrPtr[m]; - - if (Msg->number == msgno) - return Msg; - - if (Msg->number && Msg->number < msgno) // sometimes get zero msg number - return NULL; // Not found - - m--; - - } while (m > 0); - - return NULL; -} - -struct MsgInfo * FindMessageByBID(char * BID) -{ - int m = NumberofMessages; - - struct MsgInfo * Msg; - - while (m > 0) - { - Msg = MsgHddrPtr[m]; - - if (strcmp(Msg->bid, BID) == 0) - return Msg; - - m--; - } - - return NULL; -} - -VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len) -{ - unsigned char hash[50]; - unsigned char key[100]; - unsigned int i, j = 0, val1, val2; - unsigned char hostname[100]=""; - - gethostname(hostname, 100); - - strcpy(key, hostname); - strcat(key, ISPPOP3Name); - - md5(key, hash); - memcpy(&hash[16], hash, 16); // in case very long password - - // String is now encoded as hex pairs, but still need to decode old format - - for (i=0; i < len; i++) - { - if (Encrypt[i] < '0' || Encrypt[i] > 'F') - goto OldFormat; - } - - // Only '0' to 'F' - - for (i=0; i < len; i++) - { - val1 = Encrypt[i++]; - val1 -= '0'; - if (val1 > 9) - val1 -= 7; - - val2 = Encrypt[i]; - val2 -= '0'; - if (val2 > 9) - val2 -= 7; - - Pass[j] = (val1 << 4) | val2; - Pass[j] ^= hash[j]; - j++; - } - - return; - -OldFormat: - - for (i=0; i < len; i++) - { - Pass[i] = Encrypt[i] ^ hash[i]; - } - - return; -} - -int EncryptPass(char * Pass, char * Encrypt) -{ - unsigned char hash[50]; - unsigned char key[100]; - unsigned int i, val; - unsigned char hostname[100]; - unsigned char extendedpass[100]; - unsigned int passlen; - unsigned char * ptr; - - gethostname(hostname, 100); - - strcpy(key, hostname); - strcat(key, ISPPOP3Name); - - md5(key, hash); - memcpy(&hash[16], hash, 16); // in case very long password - - // if password is less than 16 chars, extend with zeros - - passlen=(int)strlen(Pass); - - strcpy(extendedpass, Pass); - - if (passlen < 16) - { - for (i=passlen+1; i <= 16; i++) - { - extendedpass[i] = 0; - } - - passlen = 16; - } - - ptr = Encrypt; - Encrypt[0] = 0; - - for (i=0; i < passlen; i++) - { - val = extendedpass[i] ^ hash[i]; - ptr += sprintf(ptr, "%02X", val); - } - - return passlen * 2; -} - - - -VOID SaveIntValue(config_setting_t * group, char * name, int value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_INT); - if(setting) - config_setting_set_int(setting, value); -} - -VOID SaveInt64Value(config_setting_t * group, char * name, long long value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_INT64); - if(setting) - config_setting_set_int64(setting, value); -} - -VOID SaveFloatValue(config_setting_t * group, char * name, double value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_FLOAT); - if (setting) - config_setting_set_float(setting, value); -} - -VOID SaveStringValue(config_setting_t * group, char * name, char * value) -{ - config_setting_t *setting; - - setting = config_setting_add(group, name, CONFIG_TYPE_STRING); - if (setting) - config_setting_set_string(setting, value); - -} - - -VOID SaveOverride(config_setting_t * group, char * name, struct Override ** values) -{ - config_setting_t *setting; - struct Override ** Calls; - char Multi[10000]; - char * ptr = &Multi[1]; - - *ptr = 0; - - if (values) - { - Calls = values; - - while(Calls[0]) - { - ptr += sprintf(ptr, "%s, %d|", Calls[0]->Call, Calls[0]->Days); - Calls++; - } - *(--ptr) = 0; - } - - setting = config_setting_add(group, name, CONFIG_TYPE_STRING); - if (setting) - config_setting_set_string(setting, &Multi[1]); - -} - - -VOID SaveMultiStringValue(config_setting_t * group, char * name, char ** values) -{ - config_setting_t *setting; - char ** Calls; - char Multi[100000]; - char * ptr = &Multi[1]; - - *ptr = 0; - - if (values) - { - Calls = values; - - while(Calls[0]) - { - strcpy(ptr, Calls[0]); - ptr += strlen(Calls[0]); - *(ptr++) = '|'; - Calls++; - } - *(--ptr) = 0; - } - - setting = config_setting_add(group, name, CONFIG_TYPE_STRING); - if (setting) - config_setting_set_string(setting, &Multi[1]); - -} - -int configSaved = 0; - -VOID SaveConfig(char * ConfigName) -{ - struct UserInfo * user; - struct BBSForwardingInfo * ForwardingInfo ; - config_setting_t *root, *group, *bbs; - int i; - char Size[80]; - struct BBSForwardingInfo DummyForwardingInfo; - char Line[1024]; - - if (configSaved == 0) - { - // only create backup once per run - - CopyConfigFile(ConfigName); - configSaved = 1; - } - - memset(&DummyForwardingInfo, 0, sizeof(struct BBSForwardingInfo)); - - // Get rid of old config before saving - - config_destroy(&cfg); - - memset((void *)&cfg, 0, sizeof(config_t)); - - config_init(&cfg); - - root = config_root_setting(&cfg); - - group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); - - SaveIntValue(group, "Streams", MaxStreams); - SaveIntValue(group, "BBSApplNum", BBSApplNum); - SaveStringValue(group, "BBSName", BBSName); - SaveStringValue(group, "SYSOPCall", SYSOPCall); - SaveStringValue(group, "H-Route", HRoute); - SaveStringValue(group, "AMPRDomain", AMPRDomain); - SaveIntValue(group, "EnableUI", EnableUI); - SaveIntValue(group, "RefuseBulls", RefuseBulls); - SaveIntValue(group, "OnlyKnown", OnlyKnown); - SaveIntValue(group, "SendSYStoSYSOPCall", SendSYStoSYSOPCall); - SaveIntValue(group, "SendBBStoSYSOPCall", SendBBStoSYSOPCall); - SaveIntValue(group, "DontHoldNewUsers", DontHoldNewUsers); - SaveIntValue(group, "DefaultNoWINLINK", DefaultNoWINLINK); - SaveIntValue(group, "AllowAnon", AllowAnon); - SaveIntValue(group, "DontNeedHomeBBS", DontNeedHomeBBS); - SaveIntValue(group, "DontCheckFromCall", DontCheckFromCall); - SaveIntValue(group, "UserCantKillT", UserCantKillT); - - SaveIntValue(group, "ForwardToMe", ForwardToMe); - SaveIntValue(group, "SMTPPort", SMTPInPort); - SaveIntValue(group, "POP3Port", POP3InPort); - SaveIntValue(group, "NNTPPort", NNTPInPort); - SaveIntValue(group, "RemoteEmail", RemoteEmail); - SaveIntValue(group, "SendAMPRDirect", SendAMPRDirect); - - SaveIntValue(group, "MailForInterval", MailForInterval); - SaveStringValue(group, "MailForText", MailForText); - - EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); - - SaveIntValue(group, "AuthenticateSMTP", SMTPAuthNeeded); - - SaveIntValue(group, "MulticastRX", MulticastRX); - - SaveIntValue(group, "SMTPGatewayEnabled", ISP_Gateway_Enabled); - SaveIntValue(group, "ISPSMTPPort", ISPSMTPPort); - SaveIntValue(group, "ISPPOP3Port", ISPPOP3Port); - SaveIntValue(group, "POP3PollingInterval", ISPPOP3Interval); - - SaveStringValue(group, "MyDomain", MyDomain); - SaveStringValue(group, "ISPSMTPName", ISPSMTPName); - SaveStringValue(group, "ISPEHLOName", ISPEHLOName); - SaveStringValue(group, "ISPPOP3Name", ISPPOP3Name); - SaveStringValue(group, "ISPAccountName", ISPAccountName); - SaveStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); - - - // Save Window Sizes - -#ifndef LINBPQ - - if (ConsoleRect.right) - { - sprintf(Size,"%d,%d,%d,%d",ConsoleRect.left, ConsoleRect.right, - ConsoleRect.top, ConsoleRect.bottom); - - SaveStringValue(group, "ConsoleSize", Size); - } - - sprintf(Size,"%d,%d,%d,%d,%d",MonitorRect.left,MonitorRect.right,MonitorRect.top,MonitorRect.bottom, hMonitor ? 1 : 0); - SaveStringValue(group, "MonitorSize", Size); - - sprintf(Size,"%d,%d,%d,%d",MainRect.left,MainRect.right,MainRect.top,MainRect.bottom); - SaveStringValue(group, "WindowSize", Size); - - SaveIntValue(group, "Bells", Bells); - SaveIntValue(group, "FlashOnBell", FlashOnBell); - SaveIntValue(group, "StripLF", StripLF); - SaveIntValue(group, "WarnWrap", WarnWrap); - SaveIntValue(group, "WrapInput", WrapInput); - SaveIntValue(group, "FlashOnConnect", FlashOnConnect); - SaveIntValue(group, "CloseWindowOnBye", CloseWindowOnBye); - -#endif - - SaveIntValue(group, "Log_BBS", LogBBS); - SaveIntValue(group, "Log_TCP", LogTCP); - - sprintf(Size,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); - SaveStringValue(group, "Version", Size); - - // Save Welcome Messages and prompts - - SaveStringValue(group, "WelcomeMsg", WelcomeMsg); - SaveStringValue(group, "NewUserWelcomeMsg", NewWelcomeMsg); - SaveStringValue(group, "ExpertWelcomeMsg", ExpertWelcomeMsg); - - SaveStringValue(group, "Prompt", Prompt); - SaveStringValue(group, "NewUserPrompt", NewPrompt); - SaveStringValue(group, "ExpertPrompt", ExpertPrompt); - SaveStringValue(group, "SignoffMsg", SignoffMsg); - - SaveMultiStringValue(group, "RejFrom", RejFrom); - SaveMultiStringValue(group, "RejTo", RejTo); - SaveMultiStringValue(group, "RejAt", RejAt); - SaveMultiStringValue(group, "RejBID", RejBID); - - SaveMultiStringValue(group, "HoldFrom", HoldFrom); - SaveMultiStringValue(group, "HoldTo", HoldTo); - SaveMultiStringValue(group, "HoldAt", HoldAt); - SaveMultiStringValue(group, "HoldBID", HoldBID); - - SaveIntValue(group, "SendWP", SendWP); - SaveIntValue(group, "SendWPType", SendWPType); - SaveIntValue(group, "FilterWPBulls", FilterWPBulls); - SaveIntValue(group, "NoWPGuesses", NoWPGuesses); - - SaveStringValue(group, "SendWPTO", SendWPTO); - SaveStringValue(group, "SendWPVIA", SendWPVIA); - - SaveMultiStringValue(group, "SendWPAddrs", SendWPAddrs); - - // Save Forwarding Config - - // Interval and Max Sizes and Aliases are not user specific - - SaveIntValue(group, "MaxTXSize", MaxTXSize); - SaveIntValue(group, "MaxRXSize", MaxRXSize); - SaveIntValue(group, "ReaddressLocal", ReaddressLocal); - SaveIntValue(group, "ReaddressReceived", ReaddressReceived); - SaveIntValue(group, "WarnNoRoute", WarnNoRoute); - SaveIntValue(group, "Localtime", Localtime); - SaveIntValue(group, "SendPtoMultiple", SendPtoMultiple); - - SaveMultiStringValue(group, "FWDAliases", AliasText); - - bbs = config_setting_add(root, "BBSForwarding", CONFIG_TYPE_GROUP); - - for (i=1; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - ForwardingInfo = user->ForwardingInfo; - - if (ForwardingInfo == NULL) - continue; - - if (memcmp(ForwardingInfo, &DummyForwardingInfo, sizeof(struct BBSForwardingInfo)) == 0) - continue; // Ignore empty records; - - if (isdigit(user->Call[0]) || user->Call[0] == '_') - { - char Key[20] = "*"; - strcat (Key, user->Call); - group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); - } - else - group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); - - SaveMultiStringValue(group, "TOCalls", ForwardingInfo->TOCalls); - SaveMultiStringValue(group, "ConnectScript", ForwardingInfo->ConnectScript); - SaveMultiStringValue(group, "ATCalls", ForwardingInfo->ATCalls); - SaveMultiStringValue(group, "HRoutes", ForwardingInfo->Haddresses); - SaveMultiStringValue(group, "HRoutesP", ForwardingInfo->HaddressesP); - SaveMultiStringValue(group, "FWDTimes", ForwardingInfo->FWDTimes); - - SaveIntValue(group, "Enabled", ForwardingInfo->Enabled); - SaveIntValue(group, "RequestReverse", ForwardingInfo->ReverseFlag); - SaveIntValue(group, "AllowBlocked", ForwardingInfo->AllowBlocked); - SaveIntValue(group, "AllowCompressed", ForwardingInfo->AllowCompressed); - SaveIntValue(group, "UseB1Protocol", ForwardingInfo->AllowB1); - SaveIntValue(group, "UseB2Protocol", ForwardingInfo->AllowB2); - SaveIntValue(group, "SendCTRLZ", ForwardingInfo->SendCTRLZ); - - SaveIntValue(group, "FWDPersonalsOnly", ForwardingInfo->PersonalOnly); - SaveIntValue(group, "FWDNewImmediately", ForwardingInfo->SendNew); - SaveIntValue(group, "FwdInterval", ForwardingInfo->FwdInterval); - SaveIntValue(group, "RevFWDInterval", ForwardingInfo->RevFwdInterval); - SaveIntValue(group, "MaxFBBBlock", ForwardingInfo->MaxFBBBlockSize); - SaveIntValue(group, "ConTimeout", ForwardingInfo->ConTimeout); - - SaveStringValue(group, "BBSHA", ForwardingInfo->BBSHA); - } - - - // Save Housekeeping config - - group = config_setting_add(root, "Housekeeping", CONFIG_TYPE_GROUP); - - SaveInt64Value(group, "LastHouseKeepingTime", LastHouseKeepingTime); - SaveInt64Value(group, "LastTrafficTime", LastTrafficTime); - SaveIntValue(group, "MaxMsgno", MaxMsgno); - SaveIntValue(group, "BidLifetime", BidLifetime); - SaveIntValue(group, "MaxAge", MaxAge); - SaveIntValue(group, "LogLifetime", LogAge); - SaveIntValue(group, "LogLifetime", LogAge); - SaveIntValue(group, "MaintInterval", MaintInterval); - SaveIntValue(group, "UserLifetime", UserLifetime); - SaveIntValue(group, "MaintTime", MaintTime); - SaveFloatValue(group, "PR", PR); - SaveFloatValue(group, "PUR", PUR); - SaveFloatValue(group, "PF", PF); - SaveFloatValue(group, "PNF", PNF); - SaveIntValue(group, "BF", BF); - SaveIntValue(group, "BNF", BNF); - SaveIntValue(group, "NTSD", NTSD); - SaveIntValue(group, "NTSF", NTSF); - SaveIntValue(group, "NTSU", NTSU); -// SaveIntValue(group, "AP", AP); -// SaveIntValue(group, "AB", AB); - SaveIntValue(group, "DeletetoRecycleBin", DeletetoRecycleBin); - SaveIntValue(group, "SuppressMaintEmail", SuppressMaintEmail); - SaveIntValue(group, "MaintSaveReg", SaveRegDuringMaint); - SaveIntValue(group, "OverrideUnsent", OverrideUnsent); - SaveIntValue(group, "SendNonDeliveryMsgs", SendNonDeliveryMsgs); - SaveIntValue(group, "GenerateTrafficReport", GenerateTrafficReport); - - SaveOverride(group, "LTFROM", LTFROM); - SaveOverride(group, "LTTO", LTTO); - SaveOverride(group, "LTAT", LTAT); - - // Save UI config - - for (i=1; i<=32; i++) - { - char Key[100]; - - sprintf(Key, "UIPort%d", i); - - group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); - - if (group) - { - SaveIntValue(group, "Enabled", UIEnabled[i]); - SaveIntValue(group, "SendMF", UIMF[i]); - SaveIntValue(group, "SendHDDR", UIHDDR[i]); - SaveIntValue(group, "SendNull", UINull[i]); - - if (UIDigi[i]) - SaveStringValue(group, "Digis", UIDigi[i]); - } - } - - // Save User Config - - bbs = config_setting_add(root, "BBSUsers", CONFIG_TYPE_GROUP); - - for (i=1; i <= NumberofUsers; i++) - { - char stats[256], stats2[256]; - struct MsgStats * Stats; - char Key[20] = "*"; - - user = UserRecPtr[i]; - - if (isdigit(user->Call[0]) || user->Call[0] == '_') - { - strcat (Key, user->Call); -// group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); - } - else - { - strcpy(Key, user->Call); -// group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); - } - /* - SaveStringValue(group, "Name", user->Name); - SaveStringValue(group, "Address", user->Address); - SaveStringValue(group, "HomeBBS", user->HomeBBS); - SaveStringValue(group, "QRA", user->QRA); - SaveStringValue(group, "pass", user->pass); - SaveStringValue(group, "ZIP", user->ZIP); - SaveStringValue(group, "CMSPass", user->CMSPass); - - SaveIntValue(group, "lastmsg", user->lastmsg); - SaveIntValue(group, "flags", user->flags); - SaveIntValue(group, "PageLen", user->PageLen); - SaveIntValue(group, "BBSNumber", user->BBSNumber); - SaveIntValue(group, "RMSSSIDBits", user->RMSSSIDBits); - SaveIntValue(group, "WebSeqNo", user->WebSeqNo); - - SaveInt64Value(group, "TimeLastConnected", user->TimeLastConnected); -*/ - Stats = &user->Total; - -// sprintf(stats, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - sprintf(stats, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", - Stats->ConnectsIn, Stats->ConnectsOut, - Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], - Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], - Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], - Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], - Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], - Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); - -// SaveStringValue(group, "Totsl", stats); - - Stats = &user->Last; - - sprintf(stats2, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", -// sprintf(stats2, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - Stats->ConnectsIn, Stats->ConnectsOut, - Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], - Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], - Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], - Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], - Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], - Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); - -// SaveStringValue(group, "Last", stats2); - - sprintf(Line,"%s^%s^%s^%s^%s^%s^%s^%d^%d^%d^%d^%d^%d^%lld^%s^%s", - user->Name, user->Address, user->HomeBBS, user->QRA, user->pass, user->ZIP, user->CMSPass, - user->lastmsg, user->flags, user->PageLen, user->BBSNumber, user->RMSSSIDBits, user->WebSeqNo, - user->TimeLastConnected, stats, stats2); - - if (strlen(Line) < 10) - continue; - - SaveStringValue(bbs, Key, Line); - } - -/* - wp = config_setting_add(root, "WP", CONFIG_TYPE_GROUP); - - for (i = 0; i <= NumberofWPrecs; i++) - { - char WPString[1024]; - long long val1, val2; - - WP = WPRecPtr[i]; - val1 = WP->last_modif; - val2 = WP->last_seen; - - sprintf(Key, "R%d", i); - - sprintf(WPString, "%s|%s|%d|%d|%d|%s|%s|%s|%s|%s|%s|%ld|%ld", - &WP->callsign[0], &WP->name[0], WP->Type, WP->changed, WP->seen, &WP->first_homebbs[0], - &WP->secnd_homebbs[0], &WP->first_zip[0], &WP->secnd_zip[0], &WP->first_qth[0], &WP->secnd_qth[0], - val1, val2); - - SaveStringValue(wp, Key, WPString); - } - - // Save Message Headers - - msgs = config_setting_add(root, "MSGS", CONFIG_TYPE_GROUP); - - memset(MsgHddrPtr[0], 0, sizeof(struct MsgInfo)); - - MsgHddrPtr[0]->type = 'X'; - MsgHddrPtr[0]->status = '2'; - MsgHddrPtr[0]->number = 0; - MsgHddrPtr[0]->length = LatestMsg; - - - for (i = 0; i <= NumberofMessages; i++) - { - Msg = MsgHddrPtr[i]; - - for (n = 0; n < NBMASK; n++) - sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); - - n = 39; - while (n >=0 && HEXString1[n] == '0') - HEXString1[n--] = 0; - - for (n = 0; n < NBMASK; n++) - sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); - - n = 39; - while (n >= 0 && HEXString2[n] == '0') - HEXString2[n--] = 0; - - sprintf(Key, "R%d", Msg->number); - - n = sprintf(Line, "%c|%c|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, - Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], - &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, - &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); - - SaveStringValue(msgs, Key, Line); - } - - // Save Bids - - msgs = config_setting_add(root, "BIDS", CONFIG_TYPE_GROUP); - - for (i=1; i <= NumberofBIDs; i++) - { - sprintf(Key, "R%s", BIDRecPtr[i]->BID); - sprintf(Line, "%d|%d", BIDRecPtr[i]->mode, BIDRecPtr[i]->u.timestamp); - SaveStringValue(msgs, Key, Line); - } - -#ifdef LINBPQ - - if(! config_write_file(&cfg,"/dev/shm/linmail.cfg.temp" )) - { - print("Error while writing file.\n"); - config_destroy(&cfg); - return; - } - - CopyFile("/dev/shm/linmail.cfg.temp", ConfigName, FALSE); - -#else -*/ - if(! config_write_file(&cfg, ConfigName)) - { - fprintf(stderr, "Error while writing file.\n"); - config_destroy(&cfg); - return; - } - -//#endif - - config_destroy(&cfg); - -/* - -#ifndef LINBPQ - - // Save a copy with current Date/Time Stamp for debugging - - { - char Backup[MAX_PATH]; - time_t LT; - struct tm * tm; - - LT = time(NULL); - tm = gmtime(<); - - sprintf(Backup,"%s.%02d%02d%02d%02d%02d.save", ConfigName, tm->tm_year-100, tm->tm_mon+1, - tm->tm_mday, tm->tm_hour, tm->tm_min); - - CopyFile(ConfigName, Backup, FALSE); // Copy to .bak - } -#endif -*/ -} - -int GetIntValue(config_setting_t * group, char * name) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - return config_setting_get_int (setting); - - return 0; -} - -long long GetInt64Value(config_setting_t * group, char * name) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - return config_setting_get_int64 (setting); - - return 0; -} - -double GetFloatValue(config_setting_t * group, char * name) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - - if (setting) - { - return config_setting_get_float (setting); - } - return 0; -} - -int GetIntValueWithDefault(config_setting_t * group, char * name, int Default) -{ - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - return config_setting_get_int (setting); - - return Default; -} - - -BOOL GetStringValue(config_setting_t * group, char * name, char * value) -{ - const char * str; - config_setting_t *setting; - - setting = config_setting_get_member (group, name); - if (setting) - { - str = config_setting_get_string (setting); - strcpy(value, str); - return TRUE; - } - value[0] = 0; - return FALSE; -} - -BOOL GetConfig(char * ConfigName) -{ - int i; - char Size[80]; - config_setting_t *setting; - const char * ptr; - - config_init(&cfg); - - /* Read the file. If there is an error, report it and exit. */ - - if(! config_read_file(&cfg, ConfigName)) - { - char Msg[256]; - sprintf(Msg, "Config File Line %d - %s\n", - config_error_line(&cfg), config_error_text(&cfg)); -#ifdef WIN32 - MessageBox(NULL, Msg, "BPQMail", MB_ICONSTOP); -#else - printf("%s", Msg); -#endif - config_destroy(&cfg); - return(EXIT_FAILURE); - } - -#if LIBCONFIG_VER_MINOR > 5 - config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); -#else - config_set_auto_convert (&cfg, 1); -#endif - - group = config_lookup (&cfg, "main"); - - if (group == NULL) - return EXIT_FAILURE; - - SMTPInPort = GetIntValue(group, "SMTPPort"); - POP3InPort = GetIntValue(group, "POP3Port"); - NNTPInPort = GetIntValue(group, "NNTPPort"); - RemoteEmail = GetIntValue(group, "RemoteEmail"); - MaxStreams = GetIntValue(group, "Streams"); - BBSApplNum = GetIntValue(group, "BBSApplNum"); - EnableUI = GetIntValue(group, "EnableUI"); - MailForInterval = GetIntValue(group, "MailForInterval"); - RefuseBulls = GetIntValue(group, "RefuseBulls"); - OnlyKnown = GetIntValue(group, "OnlyKnown"); - SendSYStoSYSOPCall = GetIntValue(group, "SendSYStoSYSOPCall"); - SendBBStoSYSOPCall = GetIntValue(group, "SendBBStoSYSOPCall"); - DontHoldNewUsers = GetIntValue(group, "DontHoldNewUsers"); - DefaultNoWINLINK = GetIntValue(group, "DefaultNoWINLINK"); - ForwardToMe = GetIntValue(group, "ForwardToMe"); - AllowAnon = GetIntValue(group, "AllowAnon"); - UserCantKillT = GetIntValue(group, "UserCantKillT"); - - DontNeedHomeBBS = GetIntValue(group, "DontNeedHomeBBS"); - DontCheckFromCall = GetIntValue(group, "DontCheckFromCall"); - MaxTXSize = GetIntValue(group, "MaxTXSize"); - MaxRXSize = GetIntValue(group, "MaxRXSize"); - ReaddressLocal = GetIntValue(group, "ReaddressLocal"); - ReaddressReceived = GetIntValue(group, "ReaddressReceived"); - WarnNoRoute = GetIntValue(group, "WarnNoRoute"); - SendPtoMultiple = GetIntValue(group, "SendPtoMultiple"); - Localtime = GetIntValue(group, "Localtime"); - AliasText = GetMultiStringValue(group, "FWDAliases"); - GetStringValue(group, "BBSName", BBSName); - GetStringValue(group, "MailForText", MailForText); - GetStringValue(group, "SYSOPCall", SYSOPCall); - GetStringValue(group, "H-Route", HRoute); - GetStringValue(group, "AMPRDomain", AMPRDomain); - SendAMPRDirect = GetIntValue(group, "SendAMPRDirect"); - ISP_Gateway_Enabled = GetIntValue(group, "SMTPGatewayEnabled"); - ISPPOP3Interval = GetIntValue(group, "POP3PollingInterval"); - GetStringValue(group, "MyDomain", MyDomain); - GetStringValue(group, "ISPSMTPName", ISPSMTPName); - GetStringValue(group, "ISPPOP3Name", ISPPOP3Name); - ISPSMTPPort = GetIntValue(group, "ISPSMTPPort"); - ISPPOP3Port = GetIntValue(group, "ISPPOP3Port"); - GetStringValue(group, "ISPAccountName", ISPAccountName); - GetStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); - GetStringValue(group, "ISPAccountName", ISPAccountName); - - sprintf(SignoffMsg, "73 de %s\r", BBSName); // Default - GetStringValue(group, "SignoffMsg", SignoffMsg); - - DecryptPass(EncryptedISPAccountPass, ISPAccountPass, (int)strlen(EncryptedISPAccountPass)); - - SMTPAuthNeeded = GetIntValue(group, "AuthenticateSMTP"); - LogBBS = GetIntValue(group, "Log_BBS"); - LogTCP = GetIntValue(group, "Log_TCP"); - - MulticastRX = GetIntValue(group, "MulticastRX"); - -#ifndef LINBPQ - - GetStringValue(group, "MonitorSize", Size); - sscanf(Size,"%d,%d,%d,%d,%d",&MonitorRect.left,&MonitorRect.right,&MonitorRect.top,&MonitorRect.bottom,&OpenMon); - - GetStringValue(group, "WindowSize", Size); - sscanf(Size,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); - - Bells = GetIntValue(group, "Bells"); - - FlashOnBell = GetIntValue(group, "FlashOnBell"); - - StripLF = GetIntValue(group, "StripLF"); - CloseWindowOnBye = GetIntValue(group, "CloseWindowOnBye"); - WarnWrap = GetIntValue(group, "WarnWrap"); - WrapInput = GetIntValue(group, "WrapInput"); - FlashOnConnect = GetIntValue(group, "FlashOnConnect"); - - GetStringValue(group, "ConsoleSize", Size); - sscanf(Size,"%d,%d,%d,%d,%d", &ConsoleRect.left, &ConsoleRect.right, - &ConsoleRect.top, &ConsoleRect.bottom,&OpenConsole); - -#endif - - // Get Welcome Messages - - setting = config_setting_get_member (group, "WelcomeMsg"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - WelcomeMsg = _strdup(ptr); - } - else - WelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); - - - setting = config_setting_get_member (group, "NewUserWelcomeMsg"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - NewWelcomeMsg = _strdup(ptr); - } - else - NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); - - - setting = config_setting_get_member (group, "ExpertWelcomeMsg"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - ExpertWelcomeMsg = _strdup(ptr); - } - else - ExpertWelcomeMsg = _strdup(""); - - // Get Prompts - - setting = config_setting_get_member (group, "Prompt"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - Prompt = _strdup(ptr); - } - else - { - Prompt = malloc(20); - sprintf(Prompt, "de %s>\r\n", BBSName); - } - - setting = config_setting_get_member (group, "NewUserPrompt"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - NewPrompt = _strdup(ptr); - } - else - { - NewPrompt = malloc(20); - sprintf(NewPrompt, "de %s>\r\n", BBSName); - } - - setting = config_setting_get_member (group, "ExpertPrompt"); - - if (setting && setting->value.sval[0]) - { - ptr = config_setting_get_string (setting); - ExpertPrompt = _strdup(ptr); - } - else - { - ExpertPrompt = malloc(20); - sprintf(ExpertPrompt, "de %s>\r\n", BBSName); - } - - TidyPrompts(); - - RejFrom = GetMultiStringValue(group, "RejFrom"); - RejTo = GetMultiStringValue(group, "RejTo"); - RejAt = GetMultiStringValue(group, "RejAt"); - RejBID = GetMultiStringValue(group, "RejBID"); - - HoldFrom = GetMultiStringValue(group, "HoldFrom"); - HoldTo = GetMultiStringValue(group, "HoldTo"); - HoldAt = GetMultiStringValue(group, "HoldAt"); - HoldBID = GetMultiStringValue(group, "HoldBID"); - - // Send WP Params - - SendWP = GetIntValue(group, "SendWP"); - SendWPType = GetIntValue(group, "SendWPType"); - - GetStringValue(group, "SendWPTO", SendWPTO); - GetStringValue(group, "SendWPVIA", SendWPVIA); - - SendWPAddrs = GetMultiStringValue(group, "SendWPAddrs"); - - FilterWPBulls = GetIntValue(group, "FilterWPBulls"); - NoWPGuesses = GetIntValue(group, "NoWPGuesses"); - - if (SendWPAddrs[0] == NULL && SendWPTO[0]) - { - // convert old format TO and VIA to entry in SendWPAddrs - - SendWPAddrs = realloc(SendWPAddrs, 8); // Add entry - - if (SendWPVIA[0]) - { - char WP[256]; - - sprintf(WP, "%s@%s", SendWPTO, SendWPVIA); - SendWPAddrs[0] = _strdup(WP); - } - else - SendWPAddrs[0] = _strdup(SendWPTO); - - - SendWPAddrs[1] = 0; - - SendWPTO[0] = 0; - SendWPVIA[0] = 0; - } - - GetStringValue(group, "Version", Size); - sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); - - for (i=1; i<=32; i++) - { - char Key[100]; - - sprintf(Key, "UIPort%d", i); - - group = config_lookup (&cfg, Key); - - if (group) - { - UIEnabled[i] = GetIntValue(group, "Enabled"); - UIMF[i] = GetIntValueWithDefault(group, "SendMF", UIEnabled[i]); - UIHDDR[i] = GetIntValueWithDefault(group, "SendHDDR", UIEnabled[i]); - UINull[i] = GetIntValue(group, "SendNull"); - Size[0] = 0; - GetStringValue(group, "Digis", Size); - if (Size[0]) - UIDigi[i] = _strdup(Size); - } - } - - group = config_lookup (&cfg, "Housekeeping"); - - if (group) - { - LastHouseKeepingTime = GetIntValue(group, "LastHouseKeepingTime"); - LastTrafficTime = GetIntValue(group, "LastTrafficTime"); - MaxMsgno = GetIntValue(group, "MaxMsgno"); - LogAge = GetIntValue(group, "LogLifetime"); - BidLifetime = GetIntValue(group, "BidLifetime"); - MaxAge = GetIntValue(group, "MaxAge"); - if (MaxAge == 0) - MaxAge = 30; - UserLifetime = GetIntValue(group, "UserLifetime"); - MaintInterval = GetIntValue(group, "MaintInterval"); - - if (MaintInterval == 0) - MaintInterval = 24; - - MaintTime = GetIntValue(group, "MaintTime"); - - PR = GetFloatValue(group, "PR"); - PUR = GetFloatValue(group, "PUR"); - PF = GetFloatValue(group, "PF"); - PNF = GetFloatValue(group, "PNF"); - - BF = GetIntValue(group, "BF"); - BNF = GetIntValue(group, "BNF"); - NTSD = GetIntValue(group, "NTSD"); - NTSU = GetIntValue(group, "NTSU"); - NTSF = GetIntValue(group, "NTSF"); -// AP = GetIntValue(group, "AP"); -// AB = GetIntValue(group, "AB"); - DeletetoRecycleBin = GetIntValue(group, "DeletetoRecycleBin"); - SuppressMaintEmail = GetIntValue(group, "SuppressMaintEmail"); - SaveRegDuringMaint = GetIntValue(group, "MaintSaveReg"); - OverrideUnsent = GetIntValue(group, "OverrideUnsent"); - SendNonDeliveryMsgs = GetIntValue(group, "SendNonDeliveryMsgs"); - OverrideUnsent = GetIntValue(group, "OverrideUnsent"); - GenerateTrafficReport = GetIntValueWithDefault(group, "GenerateTrafficReport", 1); - - LTFROM = GetOverrides(group, "LTFROM"); - LTTO = GetOverrides(group, "LTTO"); - LTAT = GetOverrides(group, "LTAT"); - } - - return EXIT_SUCCESS; -} - - -int Connected(int Stream) -{ - int n, Mask; - CIRCUIT * conn; - struct UserInfo * user = NULL; - char callsign[10]; - int port, paclen, maxframe, l4window; - char ConnectedMsg[] = "*** CONNECTED "; - char Msg[100]; - char Title[100]; - int Freq = 0; - int Mode = 0; - BPQVECSTRUC * SESS; - TRANSPORTENTRY * Sess1 = NULL, * Sess2; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (Stream == conn->BPQStream) - { - if (conn->Active) - { - // Probably an outgoing connect - - ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; - conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; - conn->ErrorCount = 0; - - if (conn->BBSFlags & RunningConnectScript) - { - // BBS Outgoing Connect - - conn->paclen = 236; - - // Run first line of connect script - - ChangeSessionIdletime(Stream, BBSIDLETIME); // Default Idletime for BBS Sessions - ProcessBBSConnectScript(conn, ConnectedMsg, 15); - return 0; - } - } - - // Incoming Connect - - // Try to find port, freq, mode, etc - -#ifdef LINBPQ - SESS = &BPQHOSTVECTOR[0]; -#else - SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; -#endif - SESS +=(Stream - 1); - - if (SESS) - Sess1 = SESS->HOSTSESSION; - - if (Sess1) - { - Sess2 = Sess1->L4CROSSLINK; - - if (Sess2) - { - // See if L2 session - if so, get info from WL2K report line - - // if Session has report info, use it - - if (Sess2->Mode) - { - Freq = Sess2->Frequency; - Mode = Sess2->Mode; - } - else if (Sess2->L4CIRCUITTYPE & L2LINK) - { - LINKTABLE * LINK = Sess2->L4TARGET.LINK; - PORTCONTROLX * PORT = LINK->LINKPORT; - - Freq = PORT->WL2KInfo.Freq; - Mode = PORT->WL2KInfo.mode; - } - else - { - if (Sess2->RMSCall[0]) - { - Freq = Sess2->Frequency; - Mode = Sess2->Mode; - } - } - } - } - - memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything - conn->Active = TRUE; - conn->BPQStream = Stream; - ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions - - conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; - conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; - conn->ErrorCount = 0; - - conn->Secure_Session = GetConnectionInfo(Stream, callsign, - &port, &conn->SessType, &paclen, &maxframe, &l4window); - - strlop(callsign, ' '); // Remove trailing spaces - - if (strcmp(&callsign[strlen(callsign) - 2], "-T") == 0) - conn->RadioOnlyMode = 'T'; - else if (strcmp(&callsign[strlen(callsign) - 2], "-R") == 0) - conn->RadioOnlyMode = 'R'; - else - conn->RadioOnlyMode = 0; - - memcpy(conn->Callsign, callsign, 10); - - strlop(callsign, '-'); // Remove any SSID - - user = LookupCall(callsign); - - if (user == NULL) - { - int Length=0; - - if (OnlyKnown) - { - // Unknown users not allowed - - n = sprintf_s(Msg, sizeof(Msg), "Incoming Connect from unknown user %s Rejected", callsign); - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - - Disconnect(Stream); - return 0; - } - - user = AllocateUserRecord(callsign); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - - if (SendNewUserMessage) - { - char * MailBuffer = malloc(100); - Length += sprintf(MailBuffer, "New User %s Connected to Mailbox on Port %d Freq %d Mode %d\r\n", callsign, port, Freq, Mode); - - sprintf(Title, "New User %s", callsign); - - SendMessageToSYSOP(Title, MailBuffer, Length); - } - - if (user == NULL) return 0; // Cant happen?? - - if (!DontHoldNewUsers) - user->flags |= F_HOLDMAIL; - - if (DefaultNoWINLINK) - user->flags |= F_NOWINLINK; - - // Always set WLE User - can't see it doing any harm - - user->flags |= F_Temp_B2_BBS; - - conn->NewUser = TRUE; - } - - user->TimeLastConnected = time(NULL); - user->Total.ConnectsIn++; - - conn->UserPointer = user; - - conn->lastmsg = user->lastmsg; - - conn->NextMessagetoForward = FirstMessageIndextoForward; - - if (paclen == 0) - { - paclen = 236; - - if (conn->SessType & Sess_PACTOR) - paclen = 100; - } - - conn->paclen = paclen; - - // Set SYSOP flag if user is defined as SYSOP and Host Session - - if (((conn->SessType & Sess_BPQHOST) == Sess_BPQHOST) && (user->flags & F_SYSOP)) - conn->sysop = TRUE; - - if (conn->Secure_Session && (user->flags & F_SYSOP)) - conn->sysop = TRUE; - - Mask = 1 << (GetApplNum(Stream) - 1); - - if (user->flags & F_Excluded) - { - n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s Rejected by Exclude Flag", user->Call); - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - Disconnect(Stream); - return 0; - } - - if (port) - n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s on Port %d Freq %d Mode %s", - user->Call, port, Freq, WL2KModes[Mode]); - else - n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); - - // Send SID and Prompt (Unless Sync) - - if (user->ForwardingInfo && user->ForwardingInfo->ConTimeout) - conn->SIDResponseTimer = user->ForwardingInfo->ConTimeout / 10; // 10 sec ticks - else - conn->SIDResponseTimer = 12; // Allow a couple of minutes for response to SID - - { - BOOL B1 = FALSE, B2 = FALSE, BIN = FALSE, BLOCKED = FALSE; - BOOL WL2KRO = FALSE; - - struct BBSForwardingInfo * ForwardingInfo; - - if (conn->RadioOnlyMode == 'R') - WL2KRO = 1; - - conn->PageLen = user->PageLen; - conn->Paging = (user->PageLen > 0); - - if (user->flags & F_Temp_B2_BBS) - { - // An RMS Express user that needs a temporary BBS struct - - if (user->ForwardingInfo == NULL) - { - // we now save the Forwarding info if BBS flag is cleared, - // so there may already be a ForwardingInfo - - user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - } - - if (user->BBSNumber == 0) - user->BBSNumber = NBBBS; - - ForwardingInfo = user->ForwardingInfo; - - ForwardingInfo->AllowCompressed = TRUE; - B1 = ForwardingInfo->AllowB1 = FALSE; - B2 = ForwardingInfo->AllowB2 = TRUE; - BLOCKED = ForwardingInfo->AllowBlocked = TRUE; - } - - if (conn->NewUser) - { - BLOCKED = TRUE; - BIN = TRUE; - B2 = TRUE; - } - - if (user->ForwardingInfo) - { - BLOCKED = user->ForwardingInfo->AllowBlocked; - if (BLOCKED) - { - BIN = user->ForwardingInfo->AllowCompressed; - B1 = user->ForwardingInfo->AllowB1; - B2 = user->ForwardingInfo->AllowB2; - } - } - - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - - if (conn->RadioOnlyMode) - nodeprintf(conn,";WL2K-Radio/Internet_Network\r"); - - if (!(conn->BBSFlags & SYNCMODE)) - { - - nodeprintf(conn, BBSSID, "BPQ-", - Ver[0], Ver[1], Ver[2], Ver[3], - BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", - BLOCKED ? "FW": "", WL2KRO ? "" : "J"); - - // if (user->flags & F_Temp_B2_BBS) - // nodeprintf(conn,";PQ: 66427529\r"); - - // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); - } - } - - if ((user->Name[0] == 0) & AllowAnon) - strcpy(user->Name, user->Call); - - if (!(conn->BBSFlags & SYNCMODE)) - { - if (user->Name[0] == 0) - { - conn->Flags |= GETTINGUSER; - BBSputs(conn, NewUserPrompt); - } - else - SendWelcomeMsg(Stream, conn, user); - } - else - { - // Seems to be a timing problem - see if this fixes it - - Sleep(500); - } - - RefreshMainWindow(); - - return 0; - } - } - - return 0; -} - -int Disconnected (int Stream) -{ - struct UserInfo * user = NULL; - CIRCUIT * conn; - int n; - char Msg[255]; - int len; - char DiscMsg[] = "DISCONNECTED "; - - for (n = 0; n <= NumberofStreams-1; n++) - { - conn=&Connections[n]; - - if (Stream == conn->BPQStream) - { - if (conn->Active == FALSE) - return 0; - - // if still running connect script, reenter it to see if - // there is an else - - if (conn->BBSFlags & RunningConnectScript) - { - // We need to see if we got as far as connnected, - // as if we have we need to reset the connect script - // over the ELSE - - struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; - char ** Scripts; - - if (ForwardingInfo->TempConnectScript) - Scripts = ForwardingInfo->TempConnectScript; - else - Scripts = ForwardingInfo->ConnectScript; - - // First see if any script left - - if (Scripts[ForwardingInfo->ScriptIndex]) - { - if (ForwardingInfo->MoreLines == FALSE) - { - // Have reached end of script, so need to set back over ELSE - - ForwardingInfo->ScriptIndex--; - ForwardingInfo->MoreLines = TRUE; - } - - // if (Scripts[ForwardingInfo->ScriptIndex] == NULL || - // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished - // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished - - - ProcessBBSConnectScript(conn, DiscMsg, 15); - return 0; - } - } - - // if sysop was chatting to user clear link -#ifndef LINBPQ - if (conn->BBSFlags & SYSOPCHAT) - { - SendUnbuffered(-1, "User has disconnected\n", 23); - BBSConsole.Console->SysopChatStream = 0; - } -#endif - ClearQueue(conn); - - if (conn->PacLinkCalls) - free(conn->PacLinkCalls); - - if (conn->InputBuffer) - { - free(conn->InputBuffer); - conn->InputBuffer = NULL; - conn->InputBufferLen = 0; - } - - if (conn->InputMode == 'B') - { - // Save partly received message for a restart - - if (conn->BBSFlags & FBBB1Mode) - if (conn->Paclink == 0) // Paclink doesn't do restarts - if (strcmp(conn->Callsign, "RMS") != 0) // Neither does RMS Packet. - if (conn->DontSaveRestartData == FALSE) - SaveFBBBinary(conn); - } - - conn->Active = FALSE; - - if (conn->FwdMsg) - conn->FwdMsg->Locked = 0; // Unlock - - RefreshMainWindow(); - - RemoveTempBIDS(conn); - - len=sprintf_s(Msg, sizeof(Msg), "%s Disconnected", conn->Callsign); - WriteLogLine(conn, '|',Msg, len, LOG_BBS); - - if (conn->FBBHeaders) - { - struct FBBHeaderLine * FBBHeader; - int n; - - for (n = 0; n < 5; n++) - { - FBBHeader = &conn->FBBHeaders[n]; - - if (FBBHeader->FwdMsg) - FBBHeader->FwdMsg->Locked = 0; // Unlock - - } - - free(conn->FBBHeaders); - conn->FBBHeaders = NULL; - } - - if (conn->UserPointer) - { - struct BBSForwardingInfo * FWDInfo = conn->UserPointer->ForwardingInfo; - - if (FWDInfo) - { - FWDInfo->Forwarding = FALSE; - -// if (FWDInfo->UserCall[0]) // Will be set if RMS -// { -// FindNextRMSUser(FWDInfo); -// } -// else - FWDInfo->FwdTimer = 0; - } - } - - conn->BBSFlags = 0; // Clear ARQ Mode - - return 0; - } - } - return 0; -} - -int DoReceivedData(int Stream) -{ - int count, InputLen; - size_t MsgLen; - int n; - CIRCUIT * conn; - struct UserInfo * user; - char * ptr, * ptr2; - char * Buffer; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - if (Stream == conn->BPQStream) - { - conn->SIDResponseTimer = 0; // Got a message, so cancel timeout. - - do - { - // May have several messages per packet, or message split over packets - - OuterLoop: - if (conn->InputLen + 1000 > conn->InputBufferLen ) // Shouldnt have lines longer than this in text mode - { - conn->InputBufferLen += 1000; - conn->InputBuffer = realloc(conn->InputBuffer, conn->InputBufferLen); - } - - GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); - - if (InputLen == 0 && conn->InputMode != 'Y') - return 0; - - conn->InputLen += InputLen; - - if (conn->InputLen == 0) return 0; - - conn->Watchdog = 900; // 15 Minutes - - if (conn->InputMode == 'Y') // YAPP - { - if (ProcessYAPPMessage(conn)) // Returns TRUE if there could be more to process - goto OuterLoop; - - return 0; - } - - if (conn->InputMode == 'B') - { - // if in OpenBCM mode, remove FF transparency - - if (conn->OpenBCM) // Telnet, so escape any 0xFF - { - unsigned char * ptr1 = conn->InputBuffer; - unsigned char * ptr2; - int Len; - unsigned char c; - - // We can come through here again for the - // same data as we wait for a full packet - // So only check last InputLen bytes - - ptr1 += (conn->InputLen - InputLen); - ptr2 = ptr1; - Len = InputLen; - - while (Len--) - { - c = *(ptr1++); - - if (conn->InTelnetExcape) // Last char was ff - { - conn->InTelnetExcape = FALSE; - continue; - } - - *(ptr2++) = c; - - if (c == 0xff) // - conn->InTelnetExcape = TRUE; - } - - conn->InputLen = (int)(ptr2 - conn->InputBuffer); - } - - UnpackFBBBinary(conn); - goto OuterLoop; - } - else - { - - loop: - - if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null - { - conn->InputLen = 0; - return 0; - } - - user = conn->UserPointer; - - if (conn->BBSFlags & (MCASTRX | SYNCMODE)) - { - // MCAST and SYNCMODE deliver full packets - - if (conn->BBSFlags & RunningConnectScript) - ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); - else - ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); - - conn->InputLen=0; - continue; - } - - // This looks for CR, CRLF, LF or CR/Null and removes any LF or NULL, - // but this relies on both arriving in same packet. - // Need to check for LF and start of packet and ignore it - // But what if client is only using LF?? - // (WLE sends SID with CRLF, other packets with CR only) - - // We don't get here on the data part of a binary transfer, so - // don't need to worry about messing up binary data. - - ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); - ptr2 = memchr(conn->InputBuffer, '\n', conn->InputLen); - - if (ptr) - conn->usingCR = 1; - - if ((ptr2 && ptr2 < ptr) || ptr == 0) // LF before CR, or no CR - ptr = ptr2; // Use LF - - if (ptr) // CR or LF in buffer - { - conn->lastLineEnd = *(ptr); - - *(ptr) = '\r'; // In case was LF - - ptr2 = &conn->InputBuffer[conn->InputLen]; - - if (++ptr == ptr2) - { - // Usual Case - single msg in buffer - - // if Length is 1 and Term is LF and normal line end is CR - // this is from a split CRLF - Ignore it - - if (conn->InputLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) - Debugprintf("BPQMail split Line End Detected"); - else - { - if (conn->BBSFlags & RunningConnectScript) - ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); - else - ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); - } - conn->InputLen=0; - } - else - { - // buffer contains more that 1 message - - MsgLen = conn->InputLen - (ptr2-ptr); - - Buffer = malloc(MsgLen + 100); - - memcpy(Buffer, conn->InputBuffer, MsgLen); - - // if Length is 1 and Term is LF and normal line end is CR - // this is from a split CRLF - Ignore it - - if (MsgLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) - Debugprintf("BPQMail split Line End Detected"); - else - { - if (conn->BBSFlags & RunningConnectScript) - ProcessBBSConnectScript(conn, Buffer, (int)MsgLen); - else - ProcessLine(conn, user, Buffer, (int)MsgLen); - } - free(Buffer); - - if (*ptr == 0 || *ptr == '\n') - { - /// CR LF or CR Null - - ptr++; - conn->InputLen--; - } - - memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); - - conn->InputLen -= (int)MsgLen; - - goto loop; - - } - } - else - { - // Could be a YAPP Header - - - if (conn->InputLen == 2 && conn->InputBuffer[0] == ENQ && conn->InputBuffer[1] == 1) // YAPP Send_Init - { - UCHAR YAPPRR[2]; - YAPPRR[0] = ACK; - YAPPRR[1] = 1; - - conn->InputMode = 'Y'; - QueueMsg(conn, YAPPRR, 2); - - conn->InputLen = 0; - return 0; - } - } - } - - } while (count > 0); - - return 0; - } - } - - // Socket not found - - return 0; - -} -int DoBBSMonitorData(int Stream) -{ -// UCHAR Buffer[1000]; - UCHAR buff[500]; - - int len = 0,count=0; - int stamp; - - do - { - stamp=GetRaw(Stream, buff,&len,&count); - - if (len == 0) return 0; - - SeeifBBSUIFrame((struct _MESSAGEX *)buff, len); - } - - while (count > 0); - - - return 0; - -} - -VOID ProcessFLARQLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) -{ - Buffer[MsgLen] = 0; - - if (MsgLen == 1 && Buffer[0] == 13) - return; - - if (strcmp(Buffer, "ARQ::ETX\r") == 0) - { - // Decode it. - - UCHAR * ptr1, * ptr2, * ptr3; - int len, linelen; - struct MsgInfo * Msg = conn->TempMsg; - time_t Date; - char FullTo[100]; - char FullFrom[100]; - char ** RecpTo = NULL; // May be several Recipients - char ** HddrTo = NULL; // May be several Recipients - char ** Via = NULL; // May be several Recipients - int LocalMsg[1000] ; // Set if Recipient is a local wl2k address - - int B2To; // Offset to To: fields in B2 header - int Recipients = 0; - int RMSMsgs = 0, BBSMsgs = 0; - -// Msg->B2Flags |= B2Msg; - - - ptr1 = conn->MailBuffer; - len = Msg->length; - ptr1[len] = 0; - - if (strstr(ptr1, "ARQ:ENCODING::")) - { - // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now - - _strupr(conn->MailBuffer); - if (strstr(conn->MailBuffer, "BBSPOLL")) - { - SendARQMail(conn); - } - - free(conn->MailBuffer); - conn->MailBuffer = NULL; - conn->MailBufferSize = 0; - - return; - } - Loop: - ptr2 = strchr(ptr1, '\r'); - - linelen = (int)(ptr2 - ptr1); - - if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: - { - char SaveFrom[100]; - char * FromHA; - - memcpy(FullFrom, ptr1, linelen); - FullFrom[linelen] = 0; - - // B2 From may now contain an @BBS - - strcpy(SaveFrom, FullFrom); - - FromHA = strlop(SaveFrom, '@'); - - if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; - - strcpy(Msg->from, &SaveFrom[6]); - - if (FromHA) - { - if (strlen(FromHA) > 39) FromHA[39] = 0; - Msg->emailfrom[0] = '@'; - strcpy(&Msg->emailfrom[1], _strupr(FromHA)); - } - - // Remove any SSID - - ptr3 = strchr(Msg->from, '-'); - if (ptr3) *ptr3 = 0; - - } - else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) - { - HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); - HddrTo[Recipients] = zalloc(100); - - memset(FullTo, 0, 99); - memcpy(FullTo, &ptr1[4], linelen-4); - memcpy(HddrTo[Recipients], ptr1, linelen+2); - LocalMsg[Recipients] = FALSE; - - _strupr(FullTo); - - B2To = (int)(ptr1 - conn->MailBuffer); - - if (_memicmp(FullTo, "RMS:", 4) == 0) - { - // remove RMS and add @winlink.org - - strcpy(FullTo, "RMS"); - strcpy(Msg->via, &FullTo[4]); - } - else - { - ptr3 = strchr(FullTo, '@'); - - if (ptr3) - { - *ptr3++ = 0; - strcpy(Msg->via, ptr3); - } - else - Msg->via[0] = 0; - } - - if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) - { - // Airmail Sends MARS messages as SMTP - - if (CheckifPacket(Msg->via)) - { - // Packet Message - - memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); - _strupr(FullTo); - _strupr(Msg->via); - - // Update the saved to: line (remove the smtp:) - - strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); - BBSMsgs++; - goto BBSMsg; - } - - // If a winlink.org address we need to convert to call - - if (_stricmp(Msg->via, "winlink.org") == 0) - { - memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); - _strupr(FullTo); - LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); - } - else - { - memcpy(Msg->via, &ptr1[9], linelen); - Msg->via[linelen - 9] = 0; - strcpy(FullTo,"RMS"); - } -// FullTo[0] = 0; - - BBSMsg: - _strupr(FullTo); - _strupr(Msg->via); - } - - if (memcmp(FullTo, "RMS:", 4) == 0) - { - // remove RMS and add @winlink.org - - memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); - strcpy(Msg->via, "winlink.org"); - sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); - } - - if (strcmp(Msg->via, "RMS") == 0) - { - // replace RMS with @winlink.org - - strcpy(Msg->via, "winlink.org"); - sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); - } - - if (strlen(FullTo) > 6) - FullTo[6] = 0; - - strlop(FullTo, '-'); - - strcpy(Msg->to, FullTo); - - if (SendBBStoSYSOPCall) - if (_stricmp(FullTo, BBSName) == 0) - strcpy(Msg->to, SYSOPCall); - - if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) - { - // No routing - check @BBS and WP - - struct UserInfo * ToUser = LookupCall(FullTo); - - Msg->via[0] = 0; // In case BPQ and not found - - if (ToUser) - { - // Local User. If Home BBS is specified, use it - - if (ToUser->HomeBBS[0]) - { - strcpy(Msg->via, ToUser->HomeBBS); - } - } - else - { - WPRecP WP = LookupWP(FullTo); - - if (WP) - { - strcpy(Msg->via, WP->first_homebbs); - - } - } - - // Fix To: address in B2 Header - - if (Msg->via[0]) - sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); - else - sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); - - } - - RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); - RecpTo[Recipients] = zalloc(10); - - Via=realloc(Via, (Recipients+1) * sizeof(void *)); - Via[Recipients] = zalloc(50); - - strcpy(Via[Recipients], Msg->via); - strcpy(RecpTo[Recipients++], FullTo); - - // Remove the To: Line from the buffer - - } - else if (_memicmp(ptr1, "Type:", 4) == 0) - { - if (ptr1[6] == 'N') - Msg->type = 'T'; // NTS - else - Msg->type = ptr1[6]; - } - else if (_memicmp(ptr1, "Subject:", 8) == 0) - { - size_t Subjlen = ptr2 - &ptr1[9]; - if (Subjlen > 60) Subjlen = 60; - memcpy(Msg->title, &ptr1[9], Subjlen); - - goto ProcessBody; - } -// else if (_memicmp(ptr1, "Body:", 4) == 0) -// { -// MsgLen = atoi(&ptr1[5]); -// StartofMsg = ptr1; -// } - else if (_memicmp(ptr1, "File:", 5) == 0) - { - Msg->B2Flags |= Attachments; - } - else if (_memicmp(ptr1, "Date:", 5) == 0) - { - struct tm rtime; - char seps[] = " ,\t\r"; - - memset(&rtime, 0, sizeof(struct tm)); - - // Date: 2009/07/25 10:08 - - sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", - &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); - - sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", - &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); - - rtime.tm_year -= 1900; - - Date = mktime(&rtime) - (time_t)_MYTIMEZONE; - - if (Date == (time_t)-1) - Date = time(NULL); - - Msg->datecreated = Date; - - } - - if (linelen) // Not Null line - { - ptr1 = ptr2 + 2; // Skip cr - goto Loop; - } - - - // Processed all headers -ProcessBody: - - ptr2 +=2; // skip crlf - - Msg->length = (int)(&conn->MailBuffer[Msg->length] - ptr2); - - memmove(conn->MailBuffer, ptr2, Msg->length); - - CreateMessageFromBuffer(conn); - - conn->BBSFlags = 0; // Clear ARQ Mode - return; - } - - // File away the data - - Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf - - if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); - - conn->TempMsg->length += MsgLen; - - return; - - // Not sure what to do yet with files, but will process emails (using text style forwarding - -/* -ARQ:FILE::flarqmail-1.eml -ARQ:EMAIL:: -ARQ:SIZE::96 -ARQ::STX -//FLARQ COMPOSER -Date: 16/01/2014 22:26:06 -To: g8bpq -From: -Subject: test message - -Hello -Hello - -ARQ::ETX -*/ - - return; -} - -VOID ProcessTextFwdLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int len) -{ - Buffer[len] = 0; -// Debugprintf(Buffer); - - // With TNC2 body prompt is a single CR, so that shouldn't be ignored. - - // If thia causes problems with other TNC PMS implementations I'll have to revisit this - -// if (len == 1 && Buffer[0] == 13) -// return; - - if (conn->Flags & SENDTITLE) - { - // Waiting for Subject: prompt - - struct MsgInfo * Msg = conn->FwdMsg; - - nodeprintf(conn, "%s\r", Msg->title); - - conn->Flags &= ~SENDTITLE; - conn->Flags |= SENDBODY; - - // New Paccom PMS (V3.2) doesn't prompt for body so drop through and send it - if ((conn->BBSFlags & NEWPACCOM) == 0) - return; - - } - - if (conn->Flags & SENDBODY) - { - // Waiting for Enter Message Prompt - - struct tm * tm; - time_t temp; - - char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); - char * MsgPtr; - int MsgLen; - int Index = 0; - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r"); - conn->FwdMsg->length = (int)strlen(MsgBytes); - } - - MsgPtr = MsgBytes; - MsgLen = conn->FwdMsg->length; - - // If a B2 Message, remove B2 Header - - if (conn->FwdMsg->B2Flags & B2Msg) - { - // Remove all B2 Headers, and all but the first part. - - MsgPtr = strstr(MsgBytes, "Body:"); - - if (MsgPtr) - { - MsgLen = atoi(&MsgPtr[5]); - MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers - - if (MsgPtr) - MsgPtr +=4; - else - MsgPtr = MsgBytes; - - } - else - MsgPtr = MsgBytes; - } - - memcpy(&temp, &conn->FwdMsg->datereceived, sizeof(time_t)); - tm = gmtime(&temp); - - nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r", - tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, - conn->FwdMsg->number, BBSName, HRoute, RlineVer); - - if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header - BBSputs(conn, "\r"); - - MsgLen = RemoveLF(MsgPtr, MsgLen); - - QueueMsg(conn, MsgPtr, MsgLen); - - if (user->ForwardingInfo->SendCTRLZ) - nodeprintf(conn, "\r\x1a"); - else - nodeprintf(conn, "\r/ex\r"); - - free(MsgBytes); - - conn->FBBMsgsSent = TRUE; - - - if (conn->FwdMsg->type == 'P') - Index = PMSG; - else if (conn->FwdMsg->type == 'B') - Index = BMSG; - else if (conn->FwdMsg->type == 'T') - Index = TMSG; - - user->Total.MsgsSent[Index]++; - user->Total.BytesForwardedOut[Index] += MsgLen; - - conn->Flags &= ~SENDBODY; - conn->Flags |= WAITPROMPT; - - return; - } - - if (conn->Flags & WAITPROMPT) - { - if (Buffer[len-2] != '>') - return; - - conn->Flags &= ~WAITPROMPT; - - clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - conn->FwdMsg->status = 'F'; // Mark as forwarded - conn->FwdMsg->datechanged=time(NULL); - } - - SaveMessageDatabase(); - - conn->UserPointer->ForwardingInfo->MsgCount--; - - // See if any more to forward - - if (FindMessagestoForward(conn) && conn->FwdMsg) - { - struct MsgInfo * Msg; - - // If we are using SETCALLTOSENDER make sure this message is from the same sender - -#ifdef LINBPQ - BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; -#else - BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; -#endif - unsigned char AXCall[7]; - - Msg = conn->FwdMsg; - ConvToAX25(Msg->from, AXCall); - if (memcmp(SESS[conn->BPQStream - 1].HOSTSESSION->L4USER, AXCall, 7) != 0) - { - Disconnect(conn->BPQStream); - return; - } - - // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ - - conn->Flags |= SENDTITLE; - - - if ((conn->BBSFlags & SETCALLTOSENDER)) - nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, - (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); - else - nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, - (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, - Msg->from, Msg->bid); - } - else - { - Disconnect(conn->BPQStream); - } - return; - } -} - - -#define N 2048 /* buffer size */ -#define F 60 /* lookahead buffer size */ -#define THRESHOLD 2 -#define NIL N /* leaf of tree */ - -extern UCHAR * infile; - -BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen); - - -VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) -{ - char * Cmd, * Arg1; - char * Context; - char seps[] = " \t\r"; - int CmdLen; - - if (_memicmp(Buffer, "POSYNCLOGON", 11) == 0) - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - conn->BBSFlags |= SYNCMODE; - conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); - - Sleep(500); - - BBSputs(conn, "OK\r"); - Flush(conn); - return; - } - - if (_memicmp(Buffer, "POSYNCHELLO", 11) == 0) - { - // This is first message received after connecting to SYNC - // Save Callsign - - char Reply[32]; - conn->BBSFlags |= SYNCMODE; - conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); - - sprintf(Reply, "POSYNCLOGON %s\r", BBSName); - BBSputs(conn, Reply); - return; - } - - if (conn->BBSFlags & SYNCMODE) - { - ProcessSyncModeMessage(conn, user, Buffer, len); - return; - } - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - // A few messages should be trapped here and result in an immediate disconnect, whatever mode I think the session is in (it could be wrong) - - // *** Protocol Error - // Already Connected - // Invalid Command - - if (_memicmp(Buffer, "Already Connected", 17) == 0 || - _memicmp(Buffer, "Invalid Command", 15) == 0 || - _memicmp(Buffer, "*** Protocol Error", 18) == 0) - { - conn->BBSFlags |= DISCONNECTING; - Disconnect(conn->BPQStream); - return; - } - - if (conn->BBSFlags & FBBForwarding) - { - ProcessFBBLine(conn, user, Buffer, len); - return; - } - - if (conn->BBSFlags & FLARQMODE) - { - ProcessFLARQLine(conn, user, Buffer, len); - return; - } - - if (conn->BBSFlags & MCASTRX) - { - ProcessMCASTLine(conn, user, Buffer, len); - return; - } - - - if (conn->BBSFlags & TEXTFORWARDING) - { - ProcessTextFwdLine(conn, user, Buffer, len); - return; - } - - // if chatting to sysop pass message to BBS console - - if (conn->BBSFlags & SYSOPCHAT) - { - SendUnbuffered(-1, Buffer,len); - return; - } - - if (conn->Flags & GETTINGMESSAGE) - { - ProcessMsgLine(conn, user, Buffer, len); - return; - } - if (conn->Flags & GETTINGTITLE) - { - ProcessMsgTitle(conn, user, Buffer, len); - return; - } - - if (conn->BBSFlags & MBLFORWARDING) - { - ProcessMBLLine(conn, user, Buffer, len); - return; - } - - if (conn->Flags & GETTINGUSER || conn->NewUser) // Could be new user but dont need name - { - if (memcmp(Buffer, ";FW:", 4) == 0 || Buffer[0] == '[') - { - struct BBSForwardingInfo * ForwardingInfo; - - conn->Flags &= ~GETTINGUSER; - - // New User is a BBS - create a temp struct for it - - if ((user->flags & (F_BBS | F_Temp_B2_BBS)) == 0) // It could already be a BBS without a user name - { - // Not defined as BBS - allocate and initialise forwarding structure - - user->flags |= F_Temp_B2_BBS; - - // An RMS Express user that needs a temporary BBS struct - - ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); - - ForwardingInfo->AllowCompressed = TRUE; - ForwardingInfo->AllowBlocked = TRUE; - conn->UserPointer->ForwardingInfo->AllowB2 = TRUE; - } - SaveUserDatabase(); - } - else - { - if (conn->Flags & GETTINGUSER) - { - conn->Flags &= ~GETTINGUSER; - if (len > 18) - len = 18; - - memcpy(user->Name, Buffer, len-1); - SendWelcomeMsg(conn->BPQStream, conn, user); - SaveUserDatabase(); - UpdateWPWithUserInfo(user); - return; - } - } - } - - // Process Command - - if (conn->Paging && (conn->LinesSent >= conn->PageLen)) - { - // Waiting for paging prompt - - if (len > 1) - { - if (_memicmp(Buffer, "Abort", 1) == 0) - { - ClearQueue(conn); - conn->LinesSent = 0; - - nodeprintf(conn, AbortedMsg); - - if (conn->UserPointer->Temp->ListSuspended) - nodeprintf(conn, "bort, , = Continue..>"); - - SendPrompt(conn, user); - return; - } - } - - conn->LinesSent = 0; - return; - } - - if (user->Temp->ListSuspended) - { - // Paging limit hit when listing. User may abort, continue, or read one or more messages - - ProcessSuspendedListCommand(conn, user, Buffer, len); - return; - } - if (len == 1) - { - SendPrompt(conn, user); - return; - } - - Buffer[len] = 0; - - if (strstr(Buffer, "ARQ:FILE:")) - { - // Message from FLARQ - - conn->BBSFlags |= FLARQMODE; - strcpy(conn->ARQFilename, &Buffer[10]); // Will need name when we decide what to do with files - - // Create a Temp Messge Stucture - - CreateMessage(conn, conn->Callsign, "", "", 'P', NULL, NULL); - - Buffer[len++] = 0x0a; // BBS Msgs stored with crlf - - if ((conn->TempMsg->length + len) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); - - conn->TempMsg->length += len; - - return; - } - if (Buffer[0] == ';') // WL2K Comment - { - if (memcmp(Buffer, ";FW:", 4) == 0) - { - // Paclink User Select (poll for list) - - char * ptr1,* ptr2, * ptr3; - int index=0; - - // Convert string to Multistring - - Buffer[len-1] = 0; - - conn->PacLinkCalls = zalloc(len*3); - - ptr1 = &Buffer[5]; - ptr2 = (char *)conn->PacLinkCalls; - ptr2 += (len * 2); - strcpy(ptr2, ptr1); - - while (ptr2) - { - ptr3 = strlop(ptr2, ' '); - - if (strlen(ptr2)) - conn->PacLinkCalls[index++] = ptr2; - - ptr2 = ptr3; - } - - return; - } - - if (memcmp(Buffer, ";FR:", 4) == 0) - { - // New Message from TriMode - Just igonre till I know what to do with it - - return; - } - - // Ignore other ';' message - - return; - } - - - - if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID - { - // If a BBS, set BBS Flag - - if (user->flags & ( F_BBS | F_Temp_B2_BBS)) - { - if (user->ForwardingInfo) - { - if (user->ForwardingInfo->Forwarding && ((conn->BBSFlags & OUTWARDCONNECT) == 0)) - { - BBSputs(conn, "Already Connected\r"); - Flush(conn); - Sleep(500); - Disconnect(conn->BPQStream); - return; - } - } - - if (user->ForwardingInfo) - { - user->ForwardingInfo->Forwarding = TRUE; - user->ForwardingInfo->FwdTimer = 0; // So we dont send to immediately - } - } - - if (user->flags & ( F_BBS | F_PMS | F_Temp_B2_BBS)) - { - Parse_SID(conn, &Buffer[1], len-4); - - if (conn->BBSFlags & FBBForwarding) - { - conn->FBBIndex = 0; // ready for first block; - conn->FBBChecksum = 0; - memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); - } - else - FBBputs(conn, ">\r"); - - } - - return; - } - - Cmd = strtok_s(Buffer, seps, &Context); - - if (Cmd == NULL) - { - if (!CheckForTooManyErrors(conn)) - BBSputs(conn, "Invalid Command\r"); - - SendPrompt(conn, user); - return; - } - - Arg1 = strtok_s(NULL, seps, &Context); - CmdLen = (int)strlen(Cmd); - - // Check List first. If any other, save last listed to user record. - - if (_memicmp(Cmd, "L", 1) == 0 && _memicmp(Cmd, "LISTFILES", 3) != 0) - { - DoListCommand(conn, user, Cmd, Arg1, FALSE, Context); - SendPrompt(conn, user); - return; - } - - if (conn->lastmsg > user->lastmsg) - { - user->lastmsg = conn->lastmsg; - SaveUserDatabase(); - } - - if (_stricmp(Cmd, "SHOWRMSPOLL") == 0) - { - DoShowRMSCmd(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "AUTH") == 0) - { - DoAuthCmd(conn, user, Arg1, Context); - return; - } - - if (_memicmp(Cmd, "Abort", 1) == 0) - { - ClearQueue(conn); - conn->LinesSent = 0; - - nodeprintf(conn, AbortedMsg); - - if (conn->UserPointer->Temp->ListSuspended) - nodeprintf(conn, "bort, , = Continue..>"); - - SendPrompt(conn, user); - return; - } - if (_memicmp(Cmd, "Bye", CmdLen) == 0 || _stricmp(Cmd, "ELSE") == 0) - { - ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); - Flush(conn); - Sleep(1000); - - if (conn->BPQStream > 0) - Disconnect(conn->BPQStream); -#ifndef LINBPQ - else - CloseConsole(conn->BPQStream); -#endif - return; - } - - if (_memicmp(Cmd, "Node", 4) == 0) - { - ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); - Flush(conn); - Sleep(1000); - - if (conn->BPQStream > 0) - ReturntoNode(conn->BPQStream); -#ifndef LINBPQ - else - CloseConsole(conn->BPQStream); -#endif - return; - } - - if (_memicmp(Cmd, "IDLETIME", 4) == 0) - { - DoSetIdleTime(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "SETNEXTMESSAGENUMBER") == 0) - { - DoSetMsgNo(conn, user, Arg1, Context); - return; - } - - if (strlen(Cmd) < 12 && _memicmp(Cmd, "D", 1) == 0) - { - DoDeliveredCommand(conn, user, Cmd, Arg1, Context); - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "K", 1) == 0) - { - DoKillCommand(conn, user, Cmd, Arg1, Context); - SendPrompt(conn, user); - return; - } - - - if (_memicmp(Cmd, "LISTFILES", 3) == 0 || _memicmp(Cmd, "FILES", 5) == 0) - { - ListFiles(conn, user, Arg1); - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "READFILE", 4) == 0) - { - ReadBBSFile(conn, user, Arg1); - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "REROUTEMSGS", 7) == 0) - { - if (conn->sysop == 0) - nodeprintf(conn, "Reroute Messages needs SYSOP status\r"); - else - { - ReRouteMessages(); - nodeprintf(conn, "Ok\r"); - } - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "YAPP", 4) == 0) - { - YAPPSendFile(conn, user, Arg1); - return; - } - - if (_memicmp(Cmd, "UH", 2) == 0 && conn->sysop) - { - DoUnholdCommand(conn, user, Cmd, Arg1, Context); - SendPrompt(conn, user); - return; - } - - if (_stricmp(Cmd, "IMPORT") == 0) - { - DoImportCmd(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "EXPORT") == 0) - { - DoExportCmd(conn, user, Arg1, Context); - return; - } - - if (_memicmp(Cmd, "I", 1) == 0) - { - char * Save; - char * MsgBytes; - - if (Arg1) - { - // User WP lookup - - DoWPLookup(conn, user, Cmd[1], Arg1); - SendPrompt(conn, user); - return; - } - - - MsgBytes = Save = ReadInfoFile("info.txt"); - if (MsgBytes) - { - int Length; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - QueueMsg(conn, MsgBytes, Length); - free(Save); - } - else - BBSputs(conn, "SYSOP has not created an INFO file\r"); - - - SendPrompt(conn, user); - return; - } - - - if (_memicmp(Cmd, "Name", CmdLen) == 0) - { - if (Arg1) - { - if (strlen(Arg1) > 17) - Arg1[17] = 0; - - strcpy(user->Name, Arg1); - UpdateWPWithUserInfo(user); - - } - - SendWelcomeMsg(conn->BPQStream, conn, user); - SaveUserDatabase(); - - return; - } - - if (_memicmp(Cmd, "OP", 2) == 0) - { - int Lines; - - // Paging Control. Param is number of lines per page - - if (Arg1) - { - Lines = atoi(Arg1); - - if (Lines) // Sanity Check - { - if (Lines < 10) - { - nodeprintf(conn,"Page Length %d is too short\r", Lines); - SendPrompt(conn, user); - return; - } - } - - user->PageLen = Lines; - conn->PageLen = Lines; - conn->Paging = (Lines > 0); - SaveUserDatabase(); - } - - nodeprintf(conn,"Page Length is %d\r", user->PageLen); - SendPrompt(conn, user); - - return; - } - - if (_memicmp(Cmd, "QTH", CmdLen) == 0) - { - if (Arg1) - { - // QTH may contain spaces, so put back together, and just split at cr - - Arg1[strlen(Arg1)] = ' '; - strtok_s(Arg1, "\r", &Context); - - if (strlen(Arg1) > 60) - Arg1[60] = 0; - - strcpy(user->Address, Arg1); - UpdateWPWithUserInfo(user); - - } - - nodeprintf(conn,"QTH is %s\r", user->Address); - SendPrompt(conn, user); - - SaveUserDatabase(); - - return; - } - - if (_memicmp(Cmd, "ZIP", CmdLen) == 0) - { - if (Arg1) - { - if (strlen(Arg1) > 8) - Arg1[8] = 0; - - strcpy(user->ZIP, _strupr(Arg1)); - UpdateWPWithUserInfo(user); - } - - nodeprintf(conn,"ZIP is %s\r", user->ZIP); - SendPrompt(conn, user); - - SaveUserDatabase(); - - return; - } - - if (_memicmp(Cmd, "CMSPASS", 7) == 0) - { - if (Arg1 == 0) - { - nodeprintf(conn,"Must specify a password\r"); - } - else - { - if (strlen(Arg1) > 15) - Arg1[15] = 0; - - strcpy(user->CMSPass, Arg1); - nodeprintf(conn,"CMS Password Set\r"); - SaveUserDatabase(); - } - - SendPrompt(conn, user); - - return; - } - - if (_memicmp(Cmd, "PASS", CmdLen) == 0) - { - if (Arg1 == 0) - { - nodeprintf(conn,"Must specify a password\r"); - } - else - { - if (strlen(Arg1) > 12) - Arg1[12] = 0; - - strcpy(user->pass, Arg1); - nodeprintf(conn,"BBS Password Set\r"); - SaveUserDatabase(); - } - - SendPrompt(conn, user); - - return; - } - - - if (_memicmp(Cmd, "R", 1) == 0) - { - DoReadCommand(conn, user, Cmd, Arg1, Context); - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "S", 1) == 0) - { - if (!DoSendCommand(conn, user, Cmd, Arg1, Context)) - SendPrompt(conn, user); - return; - } - - if ((_memicmp(Cmd, "Help", CmdLen) == 0) || (_memicmp(Cmd, "?", 1) == 0)) - { - char * Save; - char * MsgBytes = Save = ReadInfoFile("help.txt"); - - if (MsgBytes) - { - int Length; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - QueueMsg(conn, MsgBytes, Length); - free(Save); - } - else - { - BBSputs(conn, "A - Abort Output\r"); - BBSputs(conn, "B - Logoff\r"); - BBSputs(conn, "CMSPASS Password - Set CMS Password\r"); - BBSputs(conn, "D - Flag NTS Message(s) as Delivered - D num\r"); - BBSputs(conn, "HOMEBBS - Display or get HomeBBS\r"); - BBSputs(conn, "INFO - Display information about this BBS\r"); - BBSputs(conn, "I CALL - Lookup CALL in WP Allows *CALL CALL* *CALL* wildcards\r"); - BBSputs(conn, "I@ PARAM - Lookup @BBS in WP\r"); - BBSputs(conn, "IZ PARAM - Lookup Zip Codes in WP\r"); - BBSputs(conn, "IH PARAM - Lookup HA elements in WP - eg USA EU etc\r"); - - BBSputs(conn, "K - Kill Message(s) - K num, KM (Kill my read messages)\r"); - BBSputs(conn, "L - List Message(s) - \r"); - BBSputs(conn, " L = List New, LR = List New (Oldest first)\r"); - BBSputs(conn, " LM = List Mine, L> Call, L< Call, L@ = List to, from or at\r"); - BBSputs(conn, " LL num = List msg num, L num-num = List Range\r"); - BBSputs(conn, " LN LY LH LK LF L$ LD = List Message with corresponding Status\r"); - BBSputs(conn, " LB LP LT = List Mesaage with corresponding Type\r"); - BBSputs(conn, " LC = List TO fields of all active bulletins\r"); - BBSputs(conn, " You can combine most selections eg LMP, LMN LB< G8BPQ\r"); - BBSputs(conn, "LISTFILES or FILES - List files available for download\r"); - - BBSputs(conn, "N Name - Set Name\r"); - BBSputs(conn, "NODE - Return to Node\r"); - BBSputs(conn, "OP n - Set Page Length (Output will pause every n lines)\r"); - BBSputs(conn, "PASS Password - Set BBS Password\r"); - BBSputs(conn, "POLLRMS - Manage Polling for messages from RMS \r"); - BBSputs(conn, "Q QTH - Set QTH\r"); - BBSputs(conn, "R - Read Message(s) - R num \r"); - BBSputs(conn, " RM (Read new messages to me), RMR (RM oldest first)\r"); - BBSputs(conn, "READ Name - Read File\r"); - - BBSputs(conn, "S - Send Message - S or SP Send Personal, SB Send Bull, ST Send NTS,\r"); - BBSputs(conn, " SR Num - Send Reply, SC Num - Send Copy\r"); - BBSputs(conn, "X - Toggle Expert Mode\r"); - BBSputs(conn, "YAPP - Download file from BBS using YAPP protocol\r"); - if (conn->sysop) - { - BBSputs(conn, "DOHOUSEKEEPING - Run Housekeeping process\r"); - BBSputs(conn, "EU - Edit User Flags - Type EU for Help\r"); - BBSputs(conn, "EXPORT - Export messages to file - Type EXPORT for Help\r"); - BBSputs(conn, "FWD - Control Forwarding - Type FWD for Help\r"); - BBSputs(conn, "IMPORT - Import messages from file - Type IMPORT for Help\r"); - BBSputs(conn, "REROUTEMSGS - Rerun message routing process\r"); - BBSputs(conn, "SETNEXTMESSAGENUMBER - Sets next message number\r"); - BBSputs(conn, "SHOWRMSPOLL - Displays your RMS polling list\r"); - BBSputs(conn, "UH - Unhold Message(s) - UH ALL or UH num num num...\r"); - } - } - - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "Ver", CmdLen) == 0) - { - nodeprintf(conn, "BBS Version %s\rNode Version %s\r", VersionStringWithBuild, GetVersionString()); - - SendPrompt(conn, user); - return; - } - - if (_memicmp(Cmd, "HOMEBBS", CmdLen) == 0) - { - if (Arg1) - { - if (strlen(Arg1) > 40) Arg1[40] = 0; - - strcpy(user->HomeBBS, _strupr(Arg1)); - UpdateWPWithUserInfo(user); - - if (!strchr(Arg1, '.')) - BBSputs(conn, "Please enter HA with HomeBBS eg g8bpq.gbr.eu - this will help message routing\r"); - } - - nodeprintf(conn,"HomeBBS is %s\r", user->HomeBBS); - SendPrompt(conn, user); - - SaveUserDatabase(); - - return; - } - - if ((_memicmp(Cmd, "EDITUSER", 5) == 0) || (_memicmp(Cmd, "EU", 2) == 0)) - { - DoEditUserCmd(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "POLLRMS") == 0) - { - DoPollRMSCmd(conn, user, Arg1, Context); - return; - } - - if (_stricmp(Cmd, "DOHOUSEKEEPING") == 0) - { - DoHousekeepingCmd(conn, user, Arg1, Context); - return; - } - - - if (_stricmp(Cmd, "FWD") == 0) - { - DoFwdCmd(conn, user, Arg1, Context); - return; - } - - if (_memicmp(Cmd, "X", 1) == 0) - { - user->flags ^= F_Expert; - - if (user->flags & F_Expert) - BBSputs(conn, "Expert Mode\r"); - else - BBSputs(conn, "Expert Mode off\r"); - - SaveUserDatabase(); - SendPrompt(conn, user); - return; - } - - if (conn->Flags == 0) - { - if (!CheckForTooManyErrors(conn)) - BBSputs(conn, "Invalid Command\r"); - - SendPrompt(conn, user); - } - - // Send if possible - - Flush(conn); -} - -VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...) -{ - // seems to be printf to a socket - - char buff[600]; - va_list(arglist); - - va_start(arglist, format); - vsprintf(buff, format, arglist); - - BBSputs(conn, buff); -} - -// Code to delete obsolete files from Mail folder - -#ifdef WIN32 - -int DeleteRedundantMessages() -{ - WIN32_FIND_DATA ffd; - - char szDir[MAX_PATH]; - char File[MAX_PATH]; - HANDLE hFind = INVALID_HANDLE_VALUE; - int Msgno; - - // Prepare string for use with FindFile functions. First, copy the - // string to a buffer, then append '\*' to the directory name. - - strcpy(szDir, MailDir); - strcat(szDir, "\\*.mes"); - - - - // Find the first file in the directory. - - hFind = FindFirstFile(szDir, &ffd); - - if (INVALID_HANDLE_VALUE == hFind) - { - return 0; - } - - do - { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - OutputDebugString(ffd.cFileName); - } - else - { - Msgno = atoi(&ffd.cFileName[2]); - - if (MsgnotoMsg[Msgno] == 0) - { - sprintf(File, "%s/%s%c", MailDir, ffd.cFileName, 0); - Debugprintf("Tidy Mail - Delete %s\n", File); - -// if (DeletetoRecycleBin) - DeletetoRecycle(File); -// else -// DeleteFile(File); - } - } - } - while (FindNextFile(hFind, &ffd) != 0); - - FindClose(hFind); - return 0; -} - -#else - -#include - -int MsgFilter(const struct dirent * dir) -{ - return (strstr(dir->d_name, ".mes") != 0); -} - -int DeleteRedundantMessages() -{ - struct dirent **namelist; - int n; - struct stat STAT; - int Msgno = 0, res; - char File[100]; - - n = scandir("Mail", &namelist, MsgFilter, alphasort); - - if (n < 0) - perror("scandir"); - else - { - while(n--) - { - if (stat(namelist[n]->d_name, &STAT) == 0); - { - Msgno = atoi(&namelist[n]->d_name[2]); - - if (MsgnotoMsg[Msgno] == 0) - { - sprintf(File, "Mail/%s", namelist[n]->d_name); - printf("Deleting %s\n", File); - unlink(File); - } - } - free(namelist[n]); - } - free(namelist); - } - return 0; -} -#endif - -VOID TidyWelcomeMsg(char ** pPrompt) -{ - // Make sure Welcome Message doesn't ends with > - - char * Prompt = *pPrompt; - - int i = (int)strlen(Prompt) - 1; - - *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it - - Prompt = *pPrompt; - - while (Prompt[i] == 10 || Prompt[i] == 13) - { - Prompt[i--] = 0; - } - - while (i >= 0 && Prompt[i] == '>') - Prompt[i--] = 0; - - strcat(Prompt, "\r\n"); -} - -VOID TidyPrompt(char ** pPrompt) -{ - // Make sure prompt ends > CR LF - - char * Prompt = *pPrompt; - - int i = (int)strlen(Prompt) - 1; - - *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it - - Prompt = *pPrompt; - - while (Prompt[i] == 10 || Prompt[i] == 13) - { - Prompt[i--] = 0; - } - - if (Prompt[i] != '>') - strcat(Prompt, ">"); - - strcat(Prompt, "\r\n"); -} - -VOID TidyPrompts() -{ - TidyPrompt(&Prompt); - TidyPrompt(&NewPrompt); - TidyPrompt(&ExpertPrompt); -} - -BOOL SendARQMail(CIRCUIT * conn) -{ - conn->NextMessagetoForward = FirstMessageIndextoForward; - - // Send Message. There is no mechanism for reverse forwarding - - if (FindMessagestoForward(conn)) - { - struct MsgInfo * Msg; - char MsgHddr[512]; - int HddrLen; - char TimeString[64]; - char * WholeMessage; - - char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); - int MsgLen; - - if (MsgBytes == 0) - { - MsgBytes = _strdup("Message file not found\r"); - conn->FwdMsg->length = (int)strlen(MsgBytes); - } - - Msg = conn->FwdMsg; - WholeMessage = malloc(Msg->length + 512); - - FormatTime(TimeString, (time_t)Msg->datecreated); - -/* -ARQ:FILE::flarqmail-1.eml -ARQ:EMAIL:: -ARQ:SIZE::96 -ARQ::STX -//FLARQ COMPOSER -Date: 16/01/2014 22:26:06 -To: g8bpq -From: -Subject: test message - -Hello -Hello - -ARQ::ETX -*/ - Logprintf(LOG_BBS, conn, '>', "ARQ Send Msg %d From %s To %s", Msg->number, Msg->from, Msg->to); - - HddrLen = sprintf(MsgHddr, "Date: %s\nTo: %s\nFrom: %s\nSubject %s\n\n", - TimeString, Msg->to, Msg->from, Msg->title); - - MsgLen = sprintf(WholeMessage, "ARQ:FILE::Msg%s_%d\nARQ:EMAIL::\nARQ:SIZE::%d\nARQ::STX\n%s%s\nARQ::ETX\n", - BBSName, Msg->number, (int)(HddrLen + strlen(MsgBytes)), MsgHddr, MsgBytes); - - WholeMessage[MsgLen] = 0; - QueueMsg(conn,WholeMessage, MsgLen); - - free(WholeMessage); - free(MsgBytes); - - // FLARQ doesn't ACK the message, so set flag to look for all acked - - conn->BBSFlags |= ARQMAILACK; - conn->ARQClearCount = 10; // To make sure clear isn't reported too soon - - return TRUE; - } - - // Nothing to send - close - - Logprintf(LOG_BBS, conn, '>', "ARQ Send - Nothing to Send - Closing"); - - conn->CloseAfterFlush = 20; - return FALSE; -} - -char *stristr (char *ch1, char *ch2) -{ - char *chN1, *chN2; - char *chNdx; - char *chRet = NULL; - - chN1 = _strdup (ch1); - chN2 = _strdup (ch2); - if (chN1 && chN2) - { - chNdx = chN1; - while (*chNdx) - { - *chNdx = (char) tolower (*chNdx); - chNdx ++; - } - chNdx = chN2; - while (*chNdx) - { - *chNdx = (char) tolower (*chNdx); - chNdx ++; - } - - chNdx = strstr (chN1, chN2); - if (chNdx) - chRet = ch1 + (chNdx - chN1); - } - free (chN1); - free (chN2); - return chRet; -} - -#ifdef WIN32 - -void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) -{ - - WIN32_FIND_DATA ffd; - - char szDir[MAX_PATH]; - HANDLE hFind = INVALID_HANDLE_VALUE; - - // Prepare string for use with FindFile functions. First, copy the - // string to a buffer, then append '\*' to the directory name. - - strcpy(szDir, GetBPQDirectory()); - strcat(szDir, "\\BPQMailChat\\Files\\*.*"); - - // Find the first file in the directory. - - hFind = FindFirstFile(szDir, &ffd); - - if (INVALID_HANDLE_VALUE == hFind) - { - nodeprintf(conn, "No Files\r"); - return; - } - - // List all the files in the directory with some info about them. - - do - { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - {} - else - { - if (filename == NULL || stristr(ffd.cFileName, filename)) - nodeprintf(conn, "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); - } - } - while (FindNextFile(hFind, &ffd) != 0); - - FindClose(hFind); -} - -#else - -#include - -void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) -{ - struct dirent **namelist; - int n, i; - struct stat STAT; - time_t now = time(NULL); - int Age = 0, res; - char FN[256]; - - n = scandir("Files", &namelist, NULL, alphasort); - - if (n < 0) - perror("scandir"); - else - { - for (i = 0; i < n; i++) - { - sprintf(FN, "Files/%s", namelist[i]->d_name); - - if (filename == NULL || stristr(namelist[i]->d_name, filename)) - if (FN[6] != '.' && stat(FN, &STAT) == 0) - nodeprintf(conn, "%s %d\r", namelist[i]->d_name, STAT.st_size); - - free(namelist[i]); - } - free(namelist); - } - return; -} -#endif - -void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) -{ - char * MsgBytes; - - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - struct stat STAT; - - if (filename == NULL) - { - nodeprintf(conn, "Missing Filename\r"); - return; - } - - if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) - { - nodeprintf(conn, "Invalid filename\r"); - return; - } - - if (BaseDir[0]) - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); - else - sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); - - if (stat(MsgFile, &STAT) != -1) - { - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile) - { - int Length; - - MsgBytes=malloc(FileSize+1); - fread(MsgBytes, 1, FileSize, hFile); - fclose(hFile); - - MsgBytes[FileSize]=0; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - QueueMsg(conn, MsgBytes, Length); - free(MsgBytes); - - nodeprintf(conn, "\r\r[End of File %s]\r", filename); - return; - } - } - - nodeprintf(conn, "File %s not found\r", filename); -} - -VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) -{ - struct TempUserInfo * Temp = user->Temp; - - Buffer[len] = 0; - - // Command entered during listing pause. May be A R or C (or ) - - if (Buffer[0] == 'A' || Buffer[0] == 'a') - { - // Abort - - Temp->ListActive = Temp->ListSuspended = FALSE; - SendPrompt(conn, user); - return; - } - - if (_memicmp(Buffer, "R ", 2) == 0) - { - // Read Message(es) - - int msgno; - char * ptr; - char * Context; - - ptr = strtok_s(&Buffer[2], " ", &Context); - - while (ptr) - { - msgno = atoi(ptr); - ReadMessage(conn, user, msgno); - - ptr = strtok_s(NULL, " ", &Context); - } - - nodeprintf(conn, "bort, , = Continue..>"); - return; - } - - if (Buffer[0] == 'C' || Buffer[0] == 'c' || Buffer[0] == '\r' ) - { - // Resume Listing from where we left off - - DoListCommand(conn, user, Temp->LastListCommand, Temp->LastListParams, TRUE, ""); - SendPrompt(conn, user); - return; - } - - nodeprintf(conn, "bort, , = Continue..>"); - -} -/* -CreateMessageWithAttachments() -{ - int i; - char * ptr, * ptr2, * ptr3, * ptr4; - char Boundary[1000]; - BOOL Multipart = FALSE; - BOOL ALT = FALSE; - int Partlen; - char * Save; - BOOL Base64 = FALSE; - BOOL QuotedP = FALSE; - - char FileName[100][250] = {""}; - int FileLen[100]; - char * FileBody[100]; - char * MallocSave[100]; - UCHAR * NewMsg; - - int Files = 0; - - ptr = Msg; - - if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize) - { - sockptr->MailBufferSize += 2000; - sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); - - if (sockptr->MailBuffer == NULL) - { - CriticalErrorHandler("Failed to extend Message Buffer"); - shutdown(sockptr->socket, 0); - return FALSE; - } - } - - - NewMsg = sockptr->MailBuffer + 1000; - - NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]); - - for (i = 1; i < Files; i++) - { - NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]); - } - - NewMsg += sprintf(NewMsg, "\r\n"); - - for (i = 0; i < Files; i++) - { - memcpy(NewMsg, FileBody[i], FileLen[i]); - NewMsg += FileLen[i]; - free(MallocSave[i]); - NewMsg += sprintf(NewMsg, "\r\n"); - } - - *MsgLen = NewMsg - (sockptr->MailBuffer + 1000); - *Body = sockptr->MailBuffer + 1000; - - return TRUE; // B2 Message -} - -*/ -VOID CreateUserReport() -{ - struct UserInfo * User; - int i; - char Line[200]; - int len; - char File[MAX_PATH]; - FILE * hFile; - - sprintf(File, "%s/UserList.csv", BaseDir); - - hFile = fopen(File, "wb"); - - if (hFile == NULL) - { - Debugprintf("Failed to create UserList.csv"); - return; - } - - for (i=1; i <= NumberofUsers; i++) - { - User = UserRecPtr[i]; - - len = sprintf(Line, "%s,%d,%s,%x,%s,\"%s\",%x,%s,%s,%s\r\n", - User->Call, - User->lastmsg, - FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), - User->flags, - User->Name, - User->Address, - User->RMSSSIDBits, - User->HomeBBS, - User->QRA, - User->ZIP -// struct MsgStats Total; -// struct MsgStats Last; - ); - fwrite(Line, 1, len, hFile); - } - - fclose(hFile); -} - -BOOL ProcessYAPPMessage(CIRCUIT * conn) -{ - int Len = conn->InputLen; - UCHAR * Msg = conn->InputBuffer; - int pktLen = Msg[1]; - char Reply[2] = {ACK}; - int NameLen, SizeLen, OptLen; - char * ptr; - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - char Mess[255]; - int len; - char * FN = &Msg[2]; - - switch (Msg[0]) - { - case ENQ: // YAPP Send_Init - - // Shouldn't occur in session. Reset state - - Mess[0] = ACK; - Mess[1] = 1; - QueueMsg(conn, Mess, 2); - Flush(conn); - conn->InputLen = 0; - if (conn->MailBuffer) - { - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - } - return TRUE; - - case SOH: - - // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) - - // YAPPC has date/time in dos format - - if (Len < Msg[1] + 1) - return 0; - - NameLen = (int)strlen(FN); - strcpy(conn->ARQFilename, FN); - ptr = &Msg[3 + NameLen]; - SizeLen = (int)strlen(ptr); - FileSize = atoi(ptr); - - // Check file name for unsafe characters (.. / \) - - if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) - { - Mess[0] = NAK; - Mess[1] = 0; - QueueMsg(conn, Mess, 2); - Flush(conn); - len = sprintf_s(Mess, sizeof(Mess), "YAPP File Name %s invalid\r", FN); - QueueMsg(conn, Mess, len); - SendPrompt(conn, conn->UserPointer); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - - conn->InputLen = 0; - conn->InputMode = 0; - - return FALSE; - } - - OptLen = pktLen - (NameLen + SizeLen + 2); - - conn->YAPPDate = 0; - - if (OptLen >= 8) // We have a Date/Time for YAPPC - { - ptr = ptr + SizeLen + 1; - conn->YAPPDate = strtol(ptr, NULL, 16); - } - - // Check Size - - if (FileSize > MaxRXSize) - { - Mess[0] = NAK; - Mess[1] = sprintf(&Mess[2], "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); - QueueMsg(conn, Mess, Mess[1] + 2); - - Flush(conn); - - len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); - QueueMsg(conn, Mess, len); - SendPrompt(conn, conn->UserPointer); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - - conn->InputLen = 0; - conn->InputMode = 0; - - return FALSE; - } - - // Make sure file does not exist - - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); - - hFile = fopen(MsgFile, "rb"); - - if (hFile) - { - Mess[0] = NAK; - Mess[1] = sprintf(&Mess[2], "YAPP File %s already exists\r", conn->ARQFilename);; - QueueMsg(conn, Mess, Mess[1] + 2); - - Flush(conn); - - len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s already exists\r", conn->ARQFilename); - QueueMsg(conn, Mess, len); - SendPrompt(conn, conn->UserPointer); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - fclose(hFile); - - conn->InputLen = 0; - conn->InputMode = 0; - - return FALSE; - } - - - conn->MailBufferSize = FileSize; - conn->MailBuffer=malloc(FileSize); - conn->YAPPLen = 0; - - if (conn->YAPPDate) // If present use YAPPC - Reply[1] = ACK; //Receive_TPK - else - Reply[1] = 2; //Rcv_File - - QueueMsg(conn, Reply, 2); - - len = sprintf_s(Mess, sizeof(Mess), "YAPP upload to %s started", conn->ARQFilename); - WriteLogLine(conn, '!', Mess, len, LOG_BBS); - - conn->InputLen = 0; - return FALSE; - - case STX: - - // Data Packet - - // Check we have it all - - if (conn->YAPPDate) // If present use YAPPC so have checksum - { - if (pktLen > (Len - 3)) // -3 for header and checksum - return 0; // Wait for rest - } - else - { - if (pktLen > (Len - 2)) // -2 for header - return 0; // Wait for rest - } - - // Save data and remove from buffer - - // if YAPPC check checksum - - if (conn->YAPPDate) - { - UCHAR Sum = 0; - int i; - UCHAR * uptr = &Msg[2]; - - i = pktLen; - - while(i--) - Sum += *(uptr++); - - if (Sum != *uptr) - { - // Checksum Error - - Mess[0] = CAN; - Mess[1] = 0; - QueueMsg(conn, Mess, 2); - Flush(conn); - len = sprintf_s(Mess, sizeof(Mess), "YAPPC Checksum Error\r"); - QueueMsg(conn, Mess, len); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - conn->InputLen = 0; - conn->InputMode = 0; - return TRUE; - } - } - - if ((conn->YAPPLen) + pktLen > conn->MailBufferSize) - { - // Too Big ?? - - Mess[0] = CAN; - Mess[1] = 0; - QueueMsg(conn, Mess, 2); - Flush(conn); - len = sprintf_s(Mess, sizeof(Mess), "YAPP Too much data received\r"); - QueueMsg(conn, Mess, len); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - conn->InputLen = 0; - conn->InputMode = 0; - return TRUE; - } - - - memcpy(&conn->MailBuffer[conn->YAPPLen], &Msg[2], pktLen); - conn->YAPPLen += pktLen; - - if (conn->YAPPDate) - ++pktLen; // Add Checksum - - conn->InputLen -= (pktLen + 2); - memmove(conn->InputBuffer, &conn->InputBuffer[pktLen + 2], conn->InputLen); - - return TRUE; - - case ETX: - - // End Data - - - - if (conn->YAPPLen == conn->MailBufferSize) - { - // All received - - int ret; - DWORD Written = 0; - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); - -#ifdef WIN32 - hFile = CreateFile(MsgFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) - { - ret = WriteFile(hFile, conn->MailBuffer, conn->YAPPLen, &Written, NULL); - - if (conn->YAPPDate) - { - FILETIME FileTime; - struct tm TM; - struct timeval times[2]; - time_t TT; -/* - The MS-DOS date. The date is a packed value with the following format. - - cant use DosDateTimeToFileTime on Linux - - Bits Description - 0-4 Day of the month (1–31) - 5-8 Month (1 = January, 2 = February, and so on) - 9-15 Year offset from 1980 (add 1980 to get actual year) - wFatTime - The MS-DOS time. The time is a packed value with the following format. - Bits Description - 0-4 Second divided by 2 - 5-10 Minute (0–59) - 11-15 Hour (0–23 on a 24-hour clock) -*/ - memset(&TM, 0, sizeof(TM)); - - TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; - TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); - TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); - - TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); - TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; - TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; - - Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); - - TT = mktime(&TM); - times[0].tv_sec = times[1].tv_sec = - times[0].tv_usec = times[1].tv_usec = 0; - - DosDateTimeToFileTime((WORD)(conn->YAPPDate >> 16), (WORD)conn->YAPPDate & 0xFFFF, &FileTime); - ret = SetFileTime(hFile, &FileTime, &FileTime, &FileTime); - ret = GetLastError(); - - } - CloseHandle(hFile); - } -#else - - hFile = fopen(MsgFile, "wb"); - if (hFile) - { - Written = fwrite(conn->MailBuffer, 1, conn->YAPPLen, hFile); - fclose(hFile); - - if (conn->YAPPDate) - { - struct tm TM; - struct timeval times[2]; -/* - The MS-DOS date. The date is a packed value with the following format. - - cant use DosDateTimeToFileTime on Linux - - Bits Description - 0-4 Day of the month (1–31) - 5-8 Month (1 = January, 2 = February, and so on) - 9-15 Year offset from 1980 (add 1980 to get actual year) - wFatTime - The MS-DOS time. The time is a packed value with the following format. - Bits Description - 0-4 Second divided by 2 - 5-10 Minute (0–59) - 11-15 Hour (0–23 on a 24-hour clock) -*/ - memset(&TM, 0, sizeof(TM)); - - TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; - TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); - TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); - - TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); - TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; - TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; - - Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); - - times[0].tv_sec = times[1].tv_sec = mktime(&TM); - times[0].tv_usec = times[1].tv_usec = 0; - } - } -#endif - - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - - if (Written != conn->YAPPLen) - { - Mess[0] = CAN; - Mess[1] = 0; - QueueMsg(conn, Mess, 2); - Flush(conn); - len = sprintf_s(Mess, sizeof(Mess), "Failed to save YAPP File\r"); - QueueMsg(conn, Mess, len); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - conn->InputLen = 0; - conn->InputMode = 0; - } - } - - Reply[1] = 3; //Ack_EOF - QueueMsg(conn, Reply, 2); - Flush(conn); - conn->InputLen = 0; - - return TRUE; - - case EOT: - - // End Session - - Reply[1] = 4; // Ack_EOT - QueueMsg(conn, Reply, 2); - Flush(conn); - conn->InputLen = 0; - conn->InputMode = 0; - - len = sprintf_s(Mess, sizeof(Mess), "YAPP file %s received\r", conn->ARQFilename); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - QueueMsg(conn, Mess, len); - SendPrompt(conn, conn->UserPointer); - - return TRUE; - - case CAN: - - // Abort - - Mess[0] = ACK; - Mess[1] = 5; // CAN Ack - QueueMsg(conn, Mess, 2); - Flush(conn); - - if (conn->MailBuffer) - { - free(conn->MailBuffer); - conn->MailBufferSize=0; - conn->MailBuffer=0; - } - - // There may be a reason after the CAN - - len = Msg[1]; - - if (len) - { - char * errormsg = &Msg[2]; - errormsg[len] = 0; - nodeprintf(conn, "File Rejected - %s\r", errormsg); - } - else - - nodeprintf(conn, "File Rejected\r"); - - - len = sprintf_s(Mess, sizeof(Mess), "YAPP Transfer cancelled by Terminal\r"); - WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); - - conn->InputLen = 0; - conn->InputMode = 0; - conn->BBSFlags &= ~YAPPTX; - - return FALSE; - - case ACK: - - switch (Msg[1]) - { - case 1: // Rcv_Rdy - - // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) - - len = (int)strlen(conn->ARQFilename) + 3; - - strcpy(&Mess[2], conn->ARQFilename); - len += sprintf(&Mess[len], "%d", conn->MailBufferSize); - len++; // include null - Mess[0] = SOH; - Mess[1] = len - 2; - - QueueMsg(conn, Mess, len); - Flush(conn); - conn->InputLen = 0; - - return FALSE; - - case 2: - - // Start sending message - - YAPPSendData(conn); - conn->InputLen = 0; - return FALSE; - - case 3: - - // ACK EOF - Send EOT - - - Mess[0] = EOT; - Mess[1] = 1; - QueueMsg(conn, Mess, 2); - Flush(conn); - - conn->InputLen = 0; - return FALSE; - - case 4: - - // ACK EOT - - conn->InputMode = 0; - conn->BBSFlags &= ~YAPPTX; - - conn->InputLen = 0; - return FALSE; - - default: - conn->InputLen = 0; - return FALSE; - - - - } - - case NAK: - - // Either Reject or Restart - - // RE Resume NAK len R NULL (File size in ASCII) NULL - - if (conn->InputLen > 2 && Msg[2] == 'R' && Msg[3] == 0) - { - int posn = atoi(&Msg[4]); - - conn->YAPPLen += posn; - conn->MailBufferSize -= posn; - - YAPPSendData(conn); - conn->InputLen = 0; - return FALSE; - - } - - // There may be a reason after the ack - - len = Msg[1]; - - if (len) - { - char * errormsg = &Msg[2]; - errormsg[len] = 0; - nodeprintf(conn, "File Rejected - %s\r", errormsg); - } - else - - nodeprintf(conn, "File Rejected\r"); - - conn->InputMode = 0; - conn->BBSFlags &= ~YAPPTX; - conn->InputLen = 0; - SendPrompt(conn, conn->UserPointer); - return FALSE; - } - - nodeprintf(conn, "Unexpected message during YAPP Transfer. Transfer canncelled\r"); - - conn->InputMode = 0; - conn->BBSFlags &= ~YAPPTX; - conn->InputLen = 0; - SendPrompt(conn, conn->UserPointer); - - return FALSE; - -} - -void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) -{ - int FileSize; - char MsgFile[MAX_PATH]; - FILE * hFile; - struct stat STAT; - - if (filename == NULL) - { - nodeprintf(conn, "Filename missing\r"); - SendPrompt(conn, user); - return; - } - - if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) - { - nodeprintf(conn, "Invalid filename\r"); - SendPrompt(conn, user); - return; - } - - if (BaseDir[0]) - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); - else - sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); - - if (stat(MsgFile, &STAT) != -1) - { - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile) - { - char Mess[255]; - strcpy(conn->ARQFilename, filename); - conn->MailBuffer = malloc(FileSize); - conn->MailBufferSize = FileSize; - conn->YAPPLen = 0; - fread(conn->MailBuffer, 1, FileSize, hFile); - fclose(hFile); - - Mess[0] = ENQ; - Mess[1] = 1; - - QueueMsg(conn, Mess, 2); - Flush(conn); - - conn->InputMode = 'Y'; - - return; - } - } - - nodeprintf(conn, "File %s not found\r", filename); - SendPrompt(conn, user); -} - -void YAPPSendData(ConnectionInfo * conn) -{ - char Mess[258]; - - conn->BBSFlags |= YAPPTX; - - while (TXCount(conn->BPQStream) < 15) - { - int Left = conn->MailBufferSize; - - if (Left == 0) - { - // Finished - send End Data - - Mess[0] = ETX; - Mess[1] = 1; - - QueueMsg(conn, Mess, 2); - Flush(conn); - - conn->BBSFlags &= ~YAPPTX; - break; - } - - if (Left > conn->paclen - 2) // 2 byte header - Left = conn->paclen -2; - - memcpy(&Mess[2], &conn->MailBuffer[conn->YAPPLen], Left); - Mess[0] = STX; - Mess[1] = Left; - - QueueMsg(conn, Mess, Left + 2); - Flush(conn); - - conn->YAPPLen += Left; - conn->MailBufferSize -= Left; - } -} - -char * AddUser(char * Call, char * password, BOOL BBSFlag) -{ - struct UserInfo * USER; - - strlop(Call, '-'); - - if (strlen(Call) > 6) - Call[6] = 0; - - _strupr(Call); - - if (Call[0] == 0 || LookupCall(Call)) - { - return("User already exists\r\n"); - } - - USER = AllocateUserRecord(Call); - USER->Temp = zalloc(sizeof (struct TempUserInfo)); - - if (strlen(password) > 12) - password[12] = 0; - - strcpy(USER->pass, password); - - if (BBSFlag) - { - if(SetupNewBBS(USER)) - USER->flags |= F_BBS; - else - printf("Cannot set user to be a BBS - you already have 160 BBS's defined\r\n"); - } - - SaveUserDatabase(); - UpdateWPWithUserInfo(USER); - - return("User added\r\n"); -} - -// Server Support Code - -// For the moment only internal REQDIR and REQFIL. - -// May add WPSERV and user implemented servers -/* -F6FBB BBS > - SP REQDIR @ F6ABJ.FRA.EU - Title of message : - YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU - Text of message : - /EX - - F6FBB BBS > - SP REQFIL @ F6ABJ.FRA.EU - Title of message : - DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU - Text of message : - /EX - - Note Text not used. - -*/ - -VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To); - -BOOL ProcessReqDir(struct MsgInfo * Msg) -{ - char * Buffer; - int Len = 0; - char * ptr; - - // Parse title - gives directory and return address - - // YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU - - // At the moment we don't allow subdirectories but no harm handling here - - char Pattern[64]; - char * Address; - char * filename = NULL; // ?? Pattern Match ?? - -#ifdef WIN32 - - WIN32_FIND_DATA ffd; - - char szDir[MAX_PATH]; - HANDLE hFind = INVALID_HANDLE_VALUE; - -#else - - #include - - struct dirent **namelist; - int n, i; - struct stat STAT; - int res; - char FN[256]; - -#endif - - strcpy(Pattern, Msg->title); - - ptr = strchr(Pattern, '@'); - - if (ptr == NULL) - - // if we don't have return address no point - // but could we default to sender?? - - return FALSE; - - *ptr++ = 0; // Terminate Path - - strlop(Pattern, ' '); - - while (*ptr == ' ') - ptr++; // accept with or without spaces round @ - - Address = ptr; - - ptr = Buffer = malloc(MaxTXSize); - -#ifdef WIN32 - - // Prepare string for use with FindFile functions. First, copy the - // string to a buffer, then append '\*' to the directory name. - - strcpy(szDir, GetBPQDirectory()); - strcat(szDir, "\\BPQMailChat\\Files\\"); - strcat(szDir, Pattern); - - // Find the first file in the directory. - - hFind = FindFirstFile(szDir, &ffd); - - if (INVALID_HANDLE_VALUE == hFind) - { - Len = sprintf(Buffer, "No Files\r"); - } - else - { - do - { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - {} - else - { - if (filename == NULL || stristr(ffd.cFileName, filename)) - Len += sprintf(&Buffer[Len], "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); - } - } - while (FindNextFile(hFind, &ffd) != 0); - - FindClose(hFind); - } - -#else - - n = scandir("Files", &namelist, NULL, alphasort); - - if (n < 0) - perror("scandir"); - else - { - for (i = 0; i < n; i++) - { - sprintf(FN, "Files/%s", namelist[i]->d_name); - - if (filename == NULL || stristr(namelist[i]->d_name, filename)) - if (FN[6] != '.' && stat(FN, &STAT) == 0) - Len += sprintf(&Buffer[Len], "%s %d\r", namelist[i]->d_name, STAT.st_size); - - free(namelist[i]); - } - free(namelist); - } - -#endif - - // Build Message - - SendServerReply("REQDIR Reply", Buffer, Len, _strupr(Address)); - return TRUE; -} - -/* - ' Augment Message ID with the Message Pickup Station we're directing this message to. - ' - Dim strAugmentedMessageID As String - If GetMidRMS(MessageId) <> "" Then - ' The MPS RMS is already set on the message ID - strAugmentedMessageID = MessageId - strMPS = GetMidRMS(MessageId) - ' "@R" at the end of the MID means route message only via radio - If GetMidForwarding(MessageId) = "" And (blnRadioOnly Or UploadThroughInternet()) Then - strAugmentedMessageID &= "@" & strHFOnlyFlag - End If - ElseIf strMPS <> "" Then - ' Add MPS to the message ID - strAugmentedMessageID = MessageId & "@" & strMPS - ' "@R" at the end of the MID means route message only via radio - If blnRadioOnly Or UploadThroughInternet() Then - strAugmentedMessageID &= "@" & strHFOnlyFlag - End If - Else - strAugmentedMessageID = MessageId - End If - -*/ - -void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) -{ - Buffer[len] = 0; - - if (conn->Flags & GETTINGSYNCMESSAGE) - { - // Data - - if ((conn->TempMsg->length + len) > conn->MailBufferSize) - { - conn->MailBufferSize += 10000; - conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); - - if (conn->MailBuffer == NULL) - { - BBSputs(conn, "*** Failed to extend Message Buffer\r"); - conn->CloseAfterFlush = 20; // 2 Secs - - return; - } - } - - memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); - - conn->TempMsg->length += len; - - if (conn->TempMsg->length >= conn->SyncCompressedLen) - { - // Complete - decompress it - - conn->BBSFlags |= FBBCompressed; - Decode(conn, 1); - - conn->Flags &= !GETTINGSYNCMESSAGE; - - BBSputs(conn, "OK\r"); - return; - } - return; - } - - if (conn->Flags & PROPOSINGSYNCMSG) - { - // Waiting for response to TR AddMessage - - if (strcmp(Buffer, "OK\r") == 0) - { - char Msg[256]; - int n; - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - // Send the message, it has already been built - - conn->Flags &= !PROPOSINGSYNCMSG; - conn->Flags |= SENDINGSYNCMSG; - - n = sprintf_s(Msg, sizeof(Msg), "Sending SYNC message %s", conn->FwdMsg->bid); - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - - QueueMsg(conn, conn->SyncMessage, conn->SyncCompressedLen); - return; - } - - if (strcmp(Buffer, "NO\r") == 0) - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - // Message Rejected - ? duplicate - - if (conn->FwdMsg) - { - // Zap the entry - - clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); - conn->UserPointer->ForwardingInfo->MsgCount--; - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - conn->FwdMsg->status = 'F'; // Mark as forwarded - conn->FwdMsg->datechanged=time(NULL); - } - - conn->FwdMsg->Locked = 0; // Unlock - } - } - - BBSputs(conn, "BYE\r"); - conn->CloseAfterFlush = 20; // 2 Secs - conn->Flags &= !PROPOSINGSYNCMSG; - conn->BBSFlags &= ~SYNCMODE; - return; - } - - if (conn->Flags & SENDINGSYNCMSG) - { - if (strcmp(Buffer, "OK\r") == 0) - { - // Message Sent - - conn->Flags &= !SENDINGSYNCMSG; - free(conn->SyncMessage); - - if (conn->FwdMsg) - { - char Msg[256]; - int n; - - n = sprintf_s(Msg, sizeof(Msg), "SYNC message %s Sent", conn->FwdMsg->bid); - WriteLogLine(conn, '|',Msg, n, LOG_BBS); - - clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); - set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); - conn->UserPointer->ForwardingInfo->MsgCount--; - - // Only mark as forwarded if sent to all BBSs that should have it - - if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) - { - conn->FwdMsg->status = 'F'; // Mark as forwarded - conn->FwdMsg->datechanged=time(NULL); - } - - conn->FwdMsg->Locked = 0; // Unlock - } - - // drop through to send any more - } - else - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - conn->Flags &= !SENDINGSYNCMSG; - free(conn->SyncMessage); - - BBSputs(conn, "BYE\r"); - conn->CloseAfterFlush = 20; // 2 Secs - conn->BBSFlags &= ~SYNCMODE; - - return; - } - } - - if (strcmp(Buffer, "OK\r") == 0) - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - // Send Message(?s) to RMS Relay SYNC - -/* -OK ->TR AddMessage_V5JLSGH591JR 786 1219 522 True -BYE*/ - if (FindMessagestoForward(conn) && conn->FwdMsg) - { - struct MsgInfo * Msg = conn->FwdMsg; - char Buffer[128]; - char * Message; - - Message = FormatSYNCMessage(conn, Msg); - - // Need to compress it - - conn->SyncMessage = malloc(conn->SyncXMLLen + conn->SyncMsgLen + 4096); - - conn->SyncCompressedLen = Encode(Message, conn->SyncMessage, conn->SyncXMLLen + conn->SyncMsgLen, 0, 1); - - sprintf(Buffer, "TR AddMessage_%s %d %d %d True\r", // The True on end indicates compressed - Msg->bid, conn->SyncCompressedLen, conn->SyncXMLLen, conn->SyncMsgLen); - - free(Message); - - conn->Flags |= PROPOSINGSYNCMSG; - - BBSputs(conn, Buffer); - return; - } - - - BBSputs(conn, "BYE\r"); - conn->CloseAfterFlush = 20; // 2 Secs - conn->BBSFlags &= ~SYNCMODE; - return; - } - - if (memcmp(Buffer, "TR ", 2) == 0) - { - // Messages have TR_COMMAND_BID Compressed Len XML Len Bosy Len - - char * Command; - char * BIDptr; - - BIDRec * BID; - char *ptr1, *ptr2, *context; - - // TR AddMessage_1145_G8BPQ 727 1202 440 True - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - - Command = strtok_s(&Buffer[3], "_", &context); - BIDptr = strtok_s(NULL, " ", &context); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncCompressedLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncXMLLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - conn->SyncMsgLen = atoi(ptr2); - ptr2 = strtok_s(NULL, " ", &context); - - // If addmessage need to check bid doesn't exist - - if (strcmp(Command, "AddMessage") == 0) - { - strlop(BIDptr, '@'); // sometimes has @CALL@R - if (strlen(BIDptr) > 12) - BIDptr[12] = 0; - - BID = LookupBID(BIDptr); - - if (BID) - { - BBSputs(conn, "Rejected - Duplicate BID\r"); - return; - } - } - - conn->TempMsg = zalloc(sizeof(struct MsgInfo)); - - conn->Flags |= GETTINGSYNCMESSAGE; - - BBSputs(conn, "OK\r"); - return; - } - - if (memcmp(Buffer, "BYE\r", 4) == 0) - { - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - conn->CloseAfterFlush = 20; // 2 Secs - conn->BBSFlags &= ~SYNCMODE; - return; - } - - if (memcmp(Buffer, "BBS\r", 4) == 0) - { - // Out of Sync - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - conn->BBSFlags &= ~SYNCMODE; - return; - } - - WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); - WriteLogLine(conn, '<', "Unexpected SYNC Message", 23, LOG_BBS); - - BBSputs(conn, "BYE\r"); - conn->CloseAfterFlush = 20; // 2 Secs - conn->BBSFlags &= ~SYNCMODE; - return; -} -BOOL ProcessReqFile(struct MsgInfo * Msg) -{ - char FN[128]; - char * Buffer; - int Len = 0; - char * ptr; - struct stat STAT; - char MsgFile[MAX_PATH]; - FILE * hFile; - int FileSize; - char * MsgBytes; - - // Parse title - gives file and return address - - // DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU - - // At the moment we don't allow subdirectories but no harm handling here - - char * Address; - char * filename = NULL; // ?? Pattern Match ?? - - strcpy(FN, Msg->title); - - ptr = strchr(FN, '@'); - - if (ptr == NULL) - - // if we don't have return address no point - // but could we default to sender?? - - return FALSE; - - *ptr++ = 0; // Terminate Path - - strlop(FN, ' '); - - while (*ptr == ' ') - ptr++; // accept with or without spaces round @ - - Address = ptr; - - ptr = Buffer = malloc(MaxTXSize + 1); // Allow terminating Null - - // Build Message - - if (FN == NULL) - { - Len = sprintf(Buffer, "Missing Filename\r"); - } - else if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) - { - Len = sprintf(Buffer,"Invalid filename %s\r", FN); - } - else - { - if (BaseDir[0]) - sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, FN); - else - sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", FN); - - if (stat(MsgFile, &STAT) != -1) - { - FileSize = STAT.st_size; - - hFile = fopen(MsgFile, "rb"); - - if (hFile) - { - int Length; - - if (FileSize > MaxTXSize) - FileSize = MaxTXSize; // Truncate to max size - - MsgBytes=malloc(FileSize+1); - fread(MsgBytes, 1, FileSize, hFile); - fclose(hFile); - - MsgBytes[FileSize]=0; - - // Remove lf chars - - Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); - - Len = sprintf(Buffer, "%s", MsgBytes); - free(MsgBytes); - } - } - else - Len = sprintf(Buffer, "File %s not found\r", FN); - } - - SendServerReply("REQFIL Reply", Buffer, Len, _strupr(Address)); - return TRUE; -} - -BOOL CheckforMessagetoServer(struct MsgInfo * Msg) -{ - if (_stricmp(Msg->to, "REQDIR") == 0) - return ProcessReqDir(Msg); - - if (_stricmp(Msg->to, "REQFIL") == 0) - return ProcessReqFile(Msg); - - return FALSE; -} - -VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To) -{ - struct MsgInfo * Msg = AllocateMsgRecord(); - BIDRec * BIDRec; - char * Via; - char MsgFile[MAX_PATH]; - FILE * hFile; - size_t WriteLen=0; - - Msg->length = Length; - - GetSemaphore(&MsgNoSemaphore, 0); - Msg->number = ++LatestMsg; - MsgnotoMsg[Msg->number] = Msg; - - FreeSemaphore(&MsgNoSemaphore); - - strcpy(Msg->from, BBSName); - Via = strlop(To, '@'); - - if (Via) - strcpy(Msg->via, Via); - - strcpy(Msg->to, To); - strcpy(Msg->title, Title); - - Msg->type = 'P'; - Msg->status = 'N'; - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); - - BIDRec = AllocateBIDRecord(); - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - hFile = fopen(MsgFile, "wb"); - - if (hFile) - { - WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); - fclose(hFile); - } - - MatchMessagetoBBSList(Msg, NULL); - free(MailBuffer); -} - -void SendRequestSync(CIRCUIT * conn) -{ - // Only need XML Header - - char * Buffer = malloc(4096); - int Len = 0; - - struct tm *tm; - char Date[32]; - char MsgTime[32]; - time_t Time = time(NULL); - - char * Encoded; - - tm = gmtime(&Time); - - sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - - sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - Len += sprintf(&Buffer[Len], "\r\n"); - - Len += sprintf(&Buffer[Len], "\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " request_sync\r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Date); - Len += sprintf(&Buffer[Len], " %s\r\n", BBSName); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " BBSName\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", conn->SyncHost); - Len += sprintf(&Buffer[Len], " %d\r\n", conn->SyncPort); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], "\r\n"); - -/* - - - - request_sync - 20230205100652 - GI8BPQ - - - GI8BPQ - - 127.0.0.1 - 8780 - - - -*/ - - // Need to compress it - - conn->SyncXMLLen = Len; - conn->SyncMsgLen = 0; - - conn->SyncMessage = malloc(conn->SyncXMLLen + 4096); - - conn->SyncCompressedLen = Encode(Buffer, conn->SyncMessage, conn->SyncXMLLen, 0, 1); - - sprintf(Buffer, "TR RequestSync_%s_%d %d %d 0 True\r", // The True on end indicates compressed - 50, conn->SyncCompressedLen, conn->SyncXMLLen); - - free(Buffer); - - conn->Flags |= REQUESTINGSYNC; - - BBSputs(conn, Buffer); - return; -} - - -void ProcessSyncXML(CIRCUIT * conn, char * XML) -{ - // Process XML from RMS Relay Sync - - // All seem to start - - // - // - // - // - - char * Type = strstr(XML, ""); - - if (Type == NULL) - return; - - Type += strlen(""); - - if (memcmp(Type, "rms_location", 12) == 0) - { - return; - } - - - if (memcmp(Type, "request_sync", 12) == 0) - { - char * Call; - struct UserInfo * BBSREC; - - // This isn't requesting a poll, it is asking to be added as a sync partner - - Call = strstr(Type, ""); - - if (Call == NULL) - return; - - Call += 10; - strlop(Call, '<'); - BBSREC = FindBBS(Call); - - if (BBSREC == NULL) - return; - - if (BBSREC->ForwardingInfo->Forwarding == 0) - StartForwarding(BBSREC->BBSNumber, NULL); - - return; - } - - if (memcmp(Type, "remove_message", 14) == 0) - { - char * MID = strstr(Type, ""); - struct MsgInfo * Msg; - - if (MID == NULL) - return; - - MID += 11; - strlop(MID, '<'); - - strlop(MID, '@'); // sometimes has @CALL@R - if (strlen(MID) > 12) - MID[12] = 0; - - Msg = FindMessageByBID(MID); - - if (Msg == NULL) - return; - - Logprintf(LOG_BBS, conn, '|', "Killing Msg %d %s", Msg->number, Msg->bid); - - FlagAsKilled(Msg, TRUE); - return; - } - - if (memcmp(Type, "delivered", 9) == 0) - { - char * MID = strstr(Type, ""); - struct MsgInfo * Msg; - - if (MID == NULL) - return; - - MID += 11; - strlop(MID, '<'); - - strlop(MID, '@'); // sometimes has @CALL@R - if (strlen(MID) > 12) - MID[12] = 0; - - Msg = FindMessageByBID(MID); - - if (Msg == NULL) - return; - - Logprintf(LOG_BBS, conn, '|', "Message Msg %d %s Delivered", Msg->number, Msg->bid); - return; - } - - Debugprintf(Type); - return; - -/* - - - - request_sync - 20230205100652 - GI8BPQ - - - GI8BPQ - - 127.0.0.1 - 8780 - - - -} - - - - delivered - 20230205093113 - G8BPQ - - - 10845_GM8BPB - G8BPQ - G8BPQ - 3 - - - - Public Enum MessageDeliveryMethod - ' - ' Method used to deliver a message. None if the message hasn't been delivered. - ' - Unspecified = -1 - None = 0 - Telnet = 1 - CMS = 2 - Radio = 3 - Email = 4 -End Enum -*/ -} - -int ReformatSyncMessage(CIRCUIT * conn) -{ - // Message has been decompressed - reformat to look like a WLE message - - char * MsgBit; - char *ptr1, *ptr2; - int linelen; - char FullFrom[80]; - char FullTo[80]; - char BID[80]; - time_t Date; - char Mon[80]; - char Subject[80]; - int i = 0; - char * Boundary; - char * Input; - char * via = NULL; - char * NewMsg = conn->MailBuffer; - char * SaveMsg = NewMsg; - char DateString[80]; - struct tm * tm; - char Type[16] = "Private"; - char * part[100] = {""}; - char * partname[100]; - int partLen[100]; - char xml[4096]; - - // Message has an XML header then the message - - // The XML may have control info, so examine it. - - /* - Date: Mon, 25 Oct 2021 10:22:00 -0000 - From: GM8BPQ - Subject: Test - To: 2E1BGT - Message-ID: ALYJQJRXVQAO - X-Source: GM8BPQ - X-Relay: G8BPQ - MIME-Version: 1.0 - MIME-Version: 1.0 - Content-Type: multipart/mixed; boundary="boundaryBSoxlw==" - - --boundaryBSoxlw== - Content-Type: text/plain; charset="iso-8859-1" - Content-Transfer-Encoding: quoted-printable - - Hello Hello - - --boundaryBSoxlw==-- - */ - - // I think the best way is to reformat as if from Winlink Express, then pass - //through the normal B2 code. - -// WriteLogLine(conn, '<', conn->MailBuffer, conn->TempMsg->length, LOG_BBS); - - // display the message for testing - - conn->MailBuffer[conn->TempMsg->length] = 0; - -// OutputDebugString(conn->MailBuffer); - memcpy(xml, conn->MailBuffer, conn->SyncXMLLen); - xml[conn->SyncXMLLen] = 0; - - if (conn->SyncMsgLen == 0) - { - // No message, Just xml. Looks like a status report - - ProcessSyncXML(conn, xml); - return 0; - } - - MsgBit = &conn->MailBuffer[conn->SyncXMLLen]; - conn->TempMsg->length -= conn->SyncXMLLen; - - ptr1 = MsgBit; - -Loop: - - ptr2 = strchr(ptr1, '\r'); - - linelen = (int)(ptr2 - ptr1); - - if (_memicmp(ptr1, "From:", 5) == 0) - { - memcpy(FullFrom, &ptr1[6], linelen - 6); - FullFrom[linelen - 6] = 0; - } - - if (_memicmp(ptr1, "To:", 3) == 0) - { - memcpy(FullTo, &ptr1[4], linelen - 4); - FullTo[linelen - 4] = 0; - } - - else if (_memicmp(ptr1, "Subject:", 8) == 0) - { - memcpy(Subject, &ptr1[9], linelen - 9); - Subject[linelen - 9] = 0; - } - - else if (_memicmp(ptr1, "Message-ID", 10) == 0) - { - memcpy(BID, &ptr1[12], linelen - 12); - BID[linelen - 12] = 0; - } - - else if (_memicmp(ptr1, "Date:", 5) == 0) - { - struct tm rtime; - char seps[] = " ,\t\r"; - - memset(&rtime, 0, sizeof(struct tm)); - - // Date: Mon, 25 Oct 2021 10:22:00 -0000 - - sscanf(&ptr1[11], "%02d %s %04d %02d:%02d:%02d", - &rtime.tm_mday, &Mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); - - rtime.tm_year -= 1900; - - for (i = 0; i < 12; i++) - { - if (strcmp(Mon, month[i]) == 0) - break; - } - - rtime.tm_mon = i; - - Date = mktime(&rtime) - (time_t)_MYTIMEZONE; - - if (Date == (time_t)-1) - Date = time(NULL); - - } - - if (linelen) // Not Null line - { - ptr1 = ptr2 + 2; // Skip crlf - goto Loop; - } - - // Unpack Body - seems to be multipart even if only one - - // Can't we just send the whole body through ?? - // No, Attachment format is different - - // Mbo: GM8BPQ - // Body: 17 - // File: 1471 leadercoeffs.txt - - Input = MsgBit; - Boundary = initMultipartUnpack(&Input); - - i = 0; - - if (Boundary) - { - // input should be start of part - - // Find End of part - ie -- Boundary + CRLF or -- - - char * ptr, * saveptr; - char * Msgptr; - size_t BLen = strlen(Boundary); - size_t Partlen; - - saveptr = Msgptr = ptr = Input; - - while(ptr) // Just in case we run off end - { - if (*ptr == '-' && *(ptr+1) == '-') - { - if (memcmp(&ptr[2], Boundary, BLen) == 0) - { - // Found Boundary - - char * p1, *p2, *ptr3, *ptr4; - int llen; - int Base64 = 0; - int QuotedP = 0; - char * BoundaryStart = ptr; - - Partlen = ptr - Msgptr; - - ptr += (BLen + 2); // End of Boundary - - if (*ptr == '-') // Terminating Boundary - Input = NULL; - else - Input = ptr + 2; - - // Will check for quoted printable - - p1 = Msgptr; -Loop2: - p2 = strchr(p1, '\r'); - llen = (int)(p2 - p1); - - if (llen) - { - - if (_memicmp(p1, "Content-Transfer-Encoding:", 26) == 0) - { - if (_memicmp(&p1[27], "base64", 6) == 0) - Base64 = TRUE; - else if (_memicmp(&p1[27], "quoted", 6) == 0) - QuotedP = TRUE; - } - else if (_memicmp(p1, "Content-Disposition: ", 21) == 0) - { - ptr3 = strstr(&p1[21], "name"); - - if (ptr3) - { - ptr3 += 5; - if (*ptr3 == '"') ptr3++; - ptr4 = strchr(ptr3, '"'); - if (ptr4) *ptr4 = 0; - - partname[i] = ptr3; - } - } - - if (llen) // Not Null line - { - p1 = p2 + 2; // Skip crlf - goto Loop2; - } - } - - part[i] = strstr(p2, "\r\n"); // Over separator - - if (part[i]) - { - part[i] += 2; - partLen[i] = BoundaryStart - part[i] - 2; - if (QuotedP) - partLen[i] = decode_quoted_printable(part[i], partLen[i]); - else if (Base64) - { - int Len = partLen[i], NewLen; - char * ptr = part[i]; - char * ptr2 = part[i]; - - // WLE sends base64 with embedded crlf, so remove them - - while (Len-- > 0) - { - if ((*ptr) != 10 && (*ptr) != 13) - *(ptr2++) = *(ptr++); - else - ptr ++; - } - - Len = ptr2 - part[i]; - ptr = part[i]; - ptr2 = part[i]; - - while (Len > 0) - { - decodeblock(ptr, ptr2); - ptr += 4; - ptr2 += 3; - Len -= 4; - } - - NewLen = (int)(ptr2 - part[i]); - - if (*(ptr-1) == '=') - NewLen--; - - if (*(ptr-2) == '=') - NewLen--; - - partLen[i] = NewLen; - } - } - Msgptr = ptr = Input; - i++; - continue; } - - // See if more parts - } - ptr++; - } - ptr++; - } - - - // Build the message - - tm = gmtime(&Date); - - sprintf(DateString, "%04d/%02d/%02d %02d:%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - NewMsg += sprintf(NewMsg, - "MID: %s\r\n" - "Date: %s\r\n" - "Type: %s\r\n" - "From: %s\r\n", - BID, DateString, Type, FullFrom); - -// if (ToCalls) -// { -// int i; - -// for (i = 0; i < Calls; i++) -// NewMsg += sprintf(NewMsg, "To: %s\r\n", ToCalls[i]); - -// } -// else - { - NewMsg += sprintf(NewMsg, "To: %s\r\n", - FullTo); - } -// if (WebMail->CC && WebMail->CC[0]) -// NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC); - - NewMsg += sprintf(NewMsg, - "Subject: %s\r\n" - "Mbo: %s\r\n", - Subject, BBSName); - - // Write the Body: line and any File Lines - - NewMsg += sprintf(NewMsg, "Body: %d\r\n", partLen[0]); - - i = 1; - - while (part[i]) - { - NewMsg += sprintf(NewMsg, "File: %d %s\r\n", - partLen[i], partname[i]); - - i++; - } - - NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header - - // Now add parts - - i = 0; - - while (part[i]) - { - memmove(NewMsg, part[i], partLen[i]); - NewMsg += partLen[i]; - i++; - NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line between attachments - } - - conn->TempMsg->length = NewMsg - SaveMsg; - conn->TempMsg->datereceived = conn->TempMsg->datechanged = time(NULL); - conn->TempMsg->datecreated = Date; - strcpy(conn->TempMsg->bid, BID); - - if (strlen(Subject) > 60) - Subject[60] = 0; - - strcpy(conn->TempMsg->title, Subject); - - return TRUE; -} - -char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg) -{ - // First an XML Header - - char * Buffer = malloc(4096 + Msg->length); - int Len = 0; - - struct tm *tm; - char Date[32]; - char MsgTime[32]; - char Separator[33]=""; - time_t Time = time(NULL); - char * MailBuffer; - int BodyLen; - char * Encoded; - - // Get the message - may need length in header - - MailBuffer = ReadMessageFile(Msg->number); - - BodyLen = Msg->length; - - // Remove any B2 Header - - if (Msg->B2Flags & B2Msg) - { - // Remove B2 Headers (up to the File: Line) - - char * ptr; - ptr = strstr(MailBuffer, "Body:"); - if (ptr) - { - BodyLen = atoi(ptr + 5); - ptr = strstr(ptr, "\r\n\r\n"); - } - if (ptr) - { - memcpy(MailBuffer, ptr + 4, BodyLen); - MailBuffer[BodyLen] = 0; - } - } - - // encode body as quoted printable; - - Encoded = malloc(Msg->length * 3); - - BodyLen = encode_quoted_printable(MailBuffer, Encoded, BodyLen); - - // Create multipart Boundary - - CreateOneTimePassword(&Separator[0], "Key", 0); - CreateOneTimePassword(&Separator[16], "Key", 1); - - - tm = gmtime(&Time); - - sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - - tm = gmtime((time_t *)&Msg->datecreated); - - sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - Len += sprintf(&Buffer[Len], "\r\n"); - - Len += sprintf(&Buffer[Len], "\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " add_message\r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Date); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); - Len += sprintf(&Buffer[Len], " \r\n", MsgTime); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); - Len += sprintf(&Buffer[Len], " 2\r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", (Msg->B2Flags & Attachments) ? "true" : "false"); - Len += sprintf(&Buffer[Len], " %d\r\n", BodyLen); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->title); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); - Len += sprintf(&Buffer[Len], " 450443\r\n"); - Len += sprintf(&Buffer[Len], " %s\r\n", Msg->to); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " 0\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " True\r\n"); - Len += sprintf(&Buffer[Len], " False\r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], " \r\n"); - Len += sprintf(&Buffer[Len], "\r\n"); - -// Debugprintf(Buffer); - - conn->SyncXMLLen = Len; - - Len += sprintf(&Buffer[Len], "Date: Sat, 04 Feb 2023 11:19:00 +0000\r\n"); - Len += sprintf(&Buffer[Len], "From: %s\r\n", Msg->from); - Len += sprintf(&Buffer[Len], "Subject: %s\r\n", Msg->title); - Len += sprintf(&Buffer[Len], "To: %s\r\n", Msg->to); - Len += sprintf(&Buffer[Len], "Message-ID: %s\r\n", Msg->bid); -// Len += sprintf(&Buffer[Len], "X-Source: G8BPQ\r\n"); -// Len += sprintf(&Buffer[Len], "X-Location: 52.979167N, 1.125000W (GRID SQUARE)\r\n"); -// Len += sprintf(&Buffer[Len], "X-RMS-Originator: G8BPQ\r\n"); -// Len += sprintf(&Buffer[Len], "X-RMS-Path: G8BPQ@2023-02-04-11:19:29\r\n"); - Len += sprintf(&Buffer[Len], "X-Relay: %s\r\n", BBSName); - - Len += sprintf(&Buffer[Len], "MIME-Version: 1.0\r\n"); - Len += sprintf(&Buffer[Len], "Content-Type: multipart/mixed; boundary=\"%s\"\r\n", Separator); - - Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before separator - Len += sprintf(&Buffer[Len], "--%s\r\n", Separator); - Len += sprintf(&Buffer[Len], "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n"); - Len += sprintf(&Buffer[Len], "Content-Transfer-Encoding: quoted-printable\r\n"); - Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before body - - Len += sprintf(&Buffer[Len], "%s\r\n", Encoded); - Len += sprintf(&Buffer[Len], "--%s--\r\n", Separator); - - conn->SyncMsgLen = Len - conn->SyncXMLLen; - - free(Encoded); - free(MailBuffer); - - return Buffer; -} - -int encode_quoted_printable(char *s, char * out, int Len) -{ - int n = 0; - char * start = out; - - while(Len--) - { - if (n >= 73 && *s != 10 && *s != 13) - {strcpy(out, "=\r\n"); n = 0; out +=3;} - if (*s == 10 || *s == 13) {putchar(*s); n = 0;} - else if (*s<32 || *s==61 || *s>126) - out += sprintf(out, "=%02x", (unsigned char)*s); - else if (*s != 32 || (*(s+1) != 10 && *(s+1) != 13)) - {*(out++) = *s; n++;} - else n += printf("=20"); - - s++; - } - *out = 0; - - return out - start; -} - -int decode_quoted_printable(char *ptr, int len) -{ - // overwrite input with decoded version - - char * ptr2 = ptr; - char * End = ptr + len; - char * Start = ptr; - - while (ptr < End) - { - if ((*ptr) == '=') - { - char c = *(++ptr); - char d; - - c = c - 48; - if (c < 0) - { - // = CRLF as a soft break - - ptr += 2; - continue; - } - if (c > 9) c -= 7; - d = *(++ptr); - d = d - 48; - if (d > 9) d -= 7; - - *(ptr2) = c << 4 | d; - ptr2++; - ptr++; - } - else - *ptr2++ = *ptr++; - } - return ptr2 - Start; -} +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Utility Routines + +#include "bpqmail.h" +#ifdef WIN32 +#include "Winspool.h" +#else +#include +#endif + + +BOOL Bells; +BOOL FlashOnBell; // Flash instead of Beep +BOOL StripLF; + +BOOL WarnWrap; +BOOL FlashOnConnect; +BOOL WrapInput; +BOOL CloseWindowOnBye; + +RECT ConsoleRect; + +BOOL OpenConsole; +BOOL OpenMon; + +int reportNewMesageEvents = 0; + + +extern struct ConsoleInfo BBSConsole; + +extern char LOC[7]; + +//#define BBSIDLETIME 120 +//#define USERIDLETIME 300 + + +#define BBSIDLETIME 900 +#define USERIDLETIME 900 + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +UCHAR * GetLogDirectory(); +DllExport int APIENTRY SessionStateNoAck(int stream, int * state); +int RefreshWebMailIndex(); +#else +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; +typedef char * (WINAPI FAR *FARPROCZ)(); +typedef int (WINAPI FAR *FARPROCX)(); +FARPROCZ pGetLOC; +FARPROCX pRefreshWebMailIndex; + +#endif + +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); +VOID APIENTRY md5 (char *arg, unsigned char * checksum); +int APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +void DeletetoRecycle(char * FN); +VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID TidyPrompts(); +char * ReadMessageFileEx(struct MsgInfo * MsgRec); +char * APIENTRY GetBPQDirectory(); +BOOL SendARQMail(CIRCUIT * conn); +int APIENTRY ChangeSessionIdletime(int Stream, int idletime); +int APIENTRY GetApplNum(int Stream); +VOID FormatTime(char * Time, time_t cTime); +BOOL CheckifPacket(char * Via); +char * APIENTRY GetVersionString(); +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +int GetCMSHash(char * Challenge, char * Password); +BOOL SendAMPRSMTP(CIRCUIT * conn); +VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen); +VOID MCastTimer(); +VOID MCastConTimer(ConnectionInfo * conn); +int FindFreeBBSNumber(); +VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL ProcessYAPPMessage(CIRCUIT * conn); +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void YAPPSendData(ConnectionInfo * conn); +VOID CheckBBSNumber(int i); +struct UserInfo * FindAMPR(); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); +char *stristr (char *ch1, char *ch2); +BOOL CheckforMessagetoServer(struct MsgInfo * Msg); +void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled); +void ListCategories(ConnectionInfo * conn); +void RebuildNNTPList(); +long long GetInt64Value(config_setting_t * group, char * name); +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +int ReformatSyncMessage(CIRCUIT * conn); +char * initMultipartUnpack(char ** Input); +char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg); +int decode_quoted_printable(char *ptr, int len); +void decodeblock( unsigned char in[4], unsigned char out[3]); +int encode_quoted_printable(char *s, char * out, int Len); +int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); +int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); + +config_t cfg; +config_setting_t * group; + +extern ULONG BBSApplMask; + +//static int SEMCLASHES = 0; + +char SecureMsg[80] = ""; // CMS Secure Signon Response + +int NumberofStreams; + +extern char VersionStringWithBuild[50]; + +#define MaxSockets 64 + +extern struct SEM OutputSEM; + +extern ConnectionInfo Connections[MaxSockets+1]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + + +extern char MailDir[MAX_PATH]; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern BIDRec ** TempBIDRecPtr; +extern int NumberofTempBIDs; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern char ** BadWords; +extern int NumberofBadWords; +extern char * BadFile; + +extern int LatestMsg; +extern struct SEM MsgNoSemaphore; // For locking updates to LatestMsg +extern int HighestBBSNumber; + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; + +extern int ProgramErrors; + +extern BOOL MonBBS; +extern BOOL MonCHAT; +extern BOOL MonTCP; + +BOOL SendNewUserMessage = TRUE; +BOOL AllowAnon = FALSE; +BOOL UserCantKillT = FALSE; + +typedef int (WINAPI FAR *FARPROCX)(); +FARPROCX pRunEventProgram; + +int RunEventProgram(char * Program, char * Param); + + +extern BOOL EventsEnabled; + +#define BPQHOSTSTREAMS 64 + +// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 + +extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +extern char WL2KModes [54][18]; +#else +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; + + +char WL2KModes [54][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "", "Pactor 1", "", "", "Pactor 2", "", "Pactor 3", "", "", "Pactor 4", // 10 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500"}; +#endif + + + + + +FILE * LogHandle[4] = {NULL, NULL, NULL, NULL}; + +time_t LastLogTime[4] = {0, 0, 0, 0}; + +char FilesNames[4][100] = {"", "", "", ""}; + +char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; + + +BOOL OpenLogfile(int Flags) +{ + UCHAR FN[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = fopen(FN, "ab"); + +#ifndef WIN32 + + if (strcmp(FN, &FilesNames[Flags][0])) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); + unlink(SYMLINK); + strcpy(&FilesNames[Flags][0], FN); + symlink(FN, SYMLINK); + } + +#endif + + return (LogHandle[Flags] != NULL); +} + +typedef int (WINAPI FAR *FARPROCX)(); + +extern FARPROCX pDllBPQTRACE; + +struct SEM LogSEM = {0, 0}; + +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) +{ + char CRLF[2] = {0x0d,0x0a}; + struct tm * tm; + char Stamp[20]; + time_t LT; +// struct _EXCEPTION_POINTERS exinfo; + + // Write to Node BPQTRACE system + + if ((Flags == LOG_BBS || Flags == LOG_DEBUG_X) && MsgLen < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = 64; + Monframe.LENGTH = 12 + MsgLen; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], Msg, MsgLen); + Monframe.DEST[1 + MsgLen] = 0; + + time(&Monframe.Timestamp); +#ifdef LINBPQ + GetSemaphore(&Semaphore, 88); + BPQTRACE(&Monframe, FALSE); + FreeSemaphore(&Semaphore); +#else + if (pDllBPQTRACE) + pDllBPQTRACE(&Monframe, FALSE); +#endif + } +#ifndef LINBPQ + __try + { +#endif + + + +#ifndef LINBPQ + + if (hMonitor) + { + if (Flags == LOG_TCP && MonTCP) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_CHAT && MonCHAT) + { + WritetoMonitorWindow((char *)&Flag, 1); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + if (Msg[MsgLen-1] != '\r') + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_BBS && MonBBS) + { + WritetoMonitorWindow((char *)&Flag, 1); + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_DEBUG_X) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + } +#endif + + if (Flags == LOG_TCP && !LogTCP) + return; + if (Flags == LOG_BBS && !LogBBS) + return; + if (Flags == LOG_CHAT && !LogCHAT) + return; + + GetSemaphore(&LogSEM, 0); + + if (LogHandle[Flags] == NULL) + OpenLogfile(Flags); + + if (LogHandle[Flags] == NULL) + { + FreeSemaphore(&LogSEM); + return; + } + LT = time(NULL); + tm = gmtime(<); + + sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); + + fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + fwrite(call, 1, 10, LogHandle[Flags]); + } + else + fwrite(" ", 1, 10, LogHandle[Flags]); + + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + + if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') + fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); + else + fwrite(CRLF, 1, 2, LogHandle[Flags]); + + // Don't close/reopen logs every time + +// if ((LT - LastLogTime[Flags]) > 60) + { + LastLogTime[Flags] = LT; + fclose(LogHandle[Flags]); + LogHandle[Flags] = NULL; + } + FreeSemaphore(&LogSEM); + +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#endif +} + +int CriticalErrorHandler(char * error) +{ + Debugprintf("Critical Error %s", error); + ProgramErrors = 25; + CheckProgramErrors(); // Force close + return 0; +} + +BOOL CheckForTooManyErrors(ConnectionInfo * conn) +{ + conn->ErrorCount++; + + if (conn->ErrorCount > 4) + { + BBSputs(conn, "Too many errors - closing\r"); + conn->CloseAfterFlush = 20; + return TRUE; + } + return FALSE; +} + + + + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[16384]; + va_list(arglist); + int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); +#ifndef LINBPQ + WriteLogLine(NULL, '!',Mess, Len, LOG_DEBUG_X); +#endif + // #ifdef _DEBUG + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + +// #endif + return; +} + +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); + WriteLogLine(conn, InOut, Mess, Len, LogMode); + + return; +} + +struct MsgInfo * GetMsgFromNumber(int msgno) +{ + if (msgno < 1 || msgno > 999999) + return NULL; + + return MsgnotoMsg[msgno]; +} + +struct UserInfo * AllocateUserRecord(char * Call) +{ + struct UserInfo * User = zalloc(sizeof (struct UserInfo)); + + strcpy(User->Call, Call); + User->Length = sizeof (struct UserInfo); + + GetSemaphore(&AllocSemaphore, 0); + + UserRecPtr=realloc(UserRecPtr,(++NumberofUsers+1) * sizeof(void *)); + UserRecPtr[NumberofUsers]= User; + + FreeSemaphore(&AllocSemaphore); + + return User; +} + +struct MsgInfo * AllocateMsgRecord() +{ + struct MsgInfo * Msg = zalloc(sizeof (struct MsgInfo)); + + GetSemaphore(&AllocSemaphore, 0); + + MsgHddrPtr=realloc(MsgHddrPtr,(++NumberofMessages+1) * sizeof(void *)); + MsgHddrPtr[NumberofMessages] = Msg; + + FreeSemaphore(&AllocSemaphore); + + return Msg; +} + +BIDRec * AllocateBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + BIDRecPtr = realloc(BIDRecPtr,(++NumberofBIDs+1) * sizeof(void *)); + BIDRecPtr[NumberofBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +BIDRec * AllocateTempBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + TempBIDRecPtr=realloc(TempBIDRecPtr,(++NumberofTempBIDs+1) * sizeof(void *)); + TempBIDRecPtr[NumberofTempBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +struct UserInfo * LookupCall(char * Call) +{ + struct UserInfo * ptr = NULL; + int i; + + for (i=1; i <= NumberofUsers; i++) + { + ptr = UserRecPtr[i]; + + if (_stricmp(ptr->Call, Call) == 0) return ptr; + + } + + return NULL; +} + +int GetNetInt(char * Line) +{ + char temp[1024]; + char * ptr = strlop(Line, ','); + int n = atoi(Line); + if (ptr == NULL) + Line[0] = 0; + else + { + strcpy(temp, ptr); + strcpy(Line, temp); + } + return n; +} + +VOID GetUserDatabase() +{ + struct UserInfo UserRec; + + FILE * Handle; + size_t ReadLen; + struct UserInfo * user; + time_t UserLimit = time(NULL) - (UserLifetime * 86400); // Oldest user to keep + int i; + + // See if user config is in main config + + group = config_lookup (&cfg, "BBSUsers"); + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * stats; + struct MsgStats * Stats; + char * ptr, * ptr2; + + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + UserRecPtr = malloc(sizeof(void *)); + UserRecPtr[0] = malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + while (entry) + { + char call[16]; + + // entry->name is call, will have * in front if a call stating woth number + + if (entry->name[0] == '*') + strcpy(call, &entry->name[1]); + else + strcpy(call, entry->name); + + user = AllocateUserRecord(call); + + ptr = entry->value.sval; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Name, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Address, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->HomeBBS, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->QRA, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->pass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->ZIP, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->CMSPass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->lastmsg = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->flags = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->PageLen = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->BBSNumber = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->RMSSSIDBits = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->WebSeqNo = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->TimeLastConnected = atol(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) Stats = &user->Total; + stats = ptr; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + Stats = &user->Last; + stats = ptr2; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + + if ((user->flags & F_BBS) == 0) // Not BBS - Check Age + { + if (UserLifetime && user->TimeLastConnected) // Dont delete manually added Users that havent yet connected + { + if (user->TimeLastConnected < UserLimit) + { + // Too Old - ignore + + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + } + } + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + + entry = config_setting_get_elem (group, index++); + } + } + else + { + Handle = fopen(UserDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&UserRec, 0, sizeof (struct UserInfo)); + UserRec.Length = sizeof (struct UserInfo); + } + else + { + // See if format has changed + + if (UserRec.Length == 0) + { + // Old format without a Length field + + struct OldUserInfo * OldRec = (struct OldUserInfo *)&UserRec; + int Users = OldRec->ConnectsIn; // User Count in control record + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak + + Handle = fopen(UserDatabasePath, "rb"); + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); // Skip Control Record + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +OldNext: + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); + + if (ReadLen > 0) + { + if (OldRec->Call[0] < '0') + goto OldNext; // Blank record + + user = AllocateUserRecord(OldRec->Call); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + // Copy info from Old record + + user->lastmsg = OldRec->lastmsg; + user->Total.ConnectsIn = OldRec->ConnectsIn; + user->TimeLastConnected = OldRec->TimeLastConnected; + user->flags = OldRec->flags; + user->PageLen = OldRec->PageLen; + user->BBSNumber = OldRec->BBSNumber; + memcpy(user->Name, OldRec->Name, 18); + memcpy(user->Address, OldRec->Address, 61); + user->Total.MsgsReceived[0] = OldRec->MsgsReceived; + user->Total.MsgsSent[0] = OldRec->MsgsSent; + user->Total.MsgsRejectedIn[0] = OldRec->MsgsRejectedIn; // Messages we reject + user->Total.MsgsRejectedOut[0] = OldRec->MsgsRejectedOut; // Messages Rejectd by other end + user->Total.BytesForwardedIn[0] = OldRec->BytesForwardedIn; + user->Total.BytesForwardedOut[0] = OldRec->BytesForwardedOut; + user->Total.ConnectsOut = OldRec->ConnectsOut; // Forwarding Connects Out + user->RMSSSIDBits = OldRec->RMSSSIDBits; // SSID's to poll in RMS + memcpy(user->HomeBBS, OldRec->HomeBBS, 41); + memcpy(user->QRA, OldRec->QRA, 7); + memcpy(user->pass, OldRec->pass, 13); + memcpy(user->ZIP, OldRec->ZIP, 9); + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Defined as BBS - allocate and initialise forwarding structure + + // Add to BBS Chain; + + user->BBSNext = BBSChain; + BBSChain = user; + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) HighestBBSNumber = user->BBSNumber; + } + goto OldNext; + } + + SortBBSChain(); + fclose(Handle); + + return; + } + } + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +Next: + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen > 0) + { + if (UserRec.Call[0] < '0') + goto Next; // Blank record + + if (UserRec.TimeLastConnected == 0) + UserRec.TimeLastConnected = UserRec.xTimeLastConnected; + + if ((UserRec.flags & F_BBS) == 0) // Not BBS - Check Age + if (UserLifetime) // if limit set + if (UserRec.TimeLastConnected) // Dont delete manually added Users that havent yet connected + if (UserRec.TimeLastConnected < UserLimit) + goto Next; // Too Old - ignore + + user = AllocateUserRecord(UserRec.Call); + memcpy(user, &UserRec, sizeof (UserRec)); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + user->ForwardingInfo = NULL; // In case left behind on crash + user->BBSNext = NULL; + user->POP3Locked = FALSE; + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + goto Next; + } + fclose(Handle); + } + + // Setting up BBS struct has been moved until all user record + // have been read so we can fix corrupt BBSNUmber + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Add to BBS Chain; + + if (user->BBSNumber == NBBBS) // Fix corrupt records + { + user->BBSNumber = FindFreeBBSNumber(); + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + } + + user->BBSNext = BBSChain; + BBSChain = user; + +// Logprintf(LOG_BBS, NULL, '?', "BBS %s BBSNumber %d", user->Call, user->BBSNumber); + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) + HighestBBSNumber = user->BBSNumber; + } + } + + // Check for dulicate BBS numbers + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_BBS) + { + if (user->BBSNumber == 0) + user->BBSNumber = FindFreeBBSNumber(); + + CheckBBSNumber(user->BBSNumber); + } + } + + SortBBSChain(); +} + +VOID CopyUserDatabase() +{ + return; // User config now in main config file +/* + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak +*/ +} + +VOID CopyConfigFile(char * ConfigName) +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); // Move .bak to .bak.1 + + CopyFile(ConfigName, Backup1, FALSE); // Copy to .bak +} + + + +VOID SaveUserDatabase() +{ + SaveConfig(ConfigName); // User config is now in main config file + GetConfig(ConfigName); + +/* + FILE * Handle; + size_t WriteLen; + int i; + + Handle = fopen(UserDatabasePath, "wb"); + + UserRecPtr[0]->Total.ConnectsIn = NumberofUsers; + + for (i=0; i <= NumberofUsers; i++) + { + WriteLen = fwrite(UserRecPtr[i], 1, (int)sizeof (struct UserInfo), Handle); + } + + fclose(Handle); +*/ + return; +} + +VOID GetMessageDatabase() +{ + struct MsgInfo MsgRec; + FILE * Handle; + size_t ReadLen; + struct MsgInfo * Msg; + char * MsgBytes; + int FileRecsize = sizeof(struct MsgInfo); // May be changed if reformating + BOOL Reformatting = FALSE; + char HEX[3] = ""; + int n; + + // See if Message Database is in main config + + group = config_lookup (&cfg, "MSGS"); + +// group = 0; + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * ptr, * ptr2; + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + if (entry) + { + // First Record has current message number + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + ptr2 = strlop(ptr2, '|'); + if (ptr2) + LatestMsg = atoi(ptr2); + } + + entry = config_setting_get_elem (group, index++); + + while (entry) + { + // entry->name is MsgNo with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + memset(&MsgRec, 0, sizeof(struct MsgInfo)); + + MsgRec.number = atoi(&entry->name[1]); + MsgRec.type = ptr[0]; + + ptr = ptr2; + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + ptr2 = strlop(ptr, '|'); + MsgRec.status = ptr[0]; + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.length = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datereceived = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bbsfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.via, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.from, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.to, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bid, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.B2Flags = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datecreated = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datechanged = atol(ptr); + + ptr = ptr2; + if (ptr) ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.fbbs[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.forw[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.emailfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.UTF8 = atoi(ptr); + + ptr = ptr2; + + if (ptr) + { + strcpy(MsgRec.title, ptr); + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + free(MsgBytes); + Msg = AllocateMsgRecord(); + memcpy(Msg, &MsgRec, sizeof (MsgRec)); + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + BuildNNTPList(Msg); // Build NNTP Groups list + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + } + } + entry = config_setting_get_elem (group, index++); + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + return; + } + + Handle = fopen(MsgDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + return; + } + + // Get First Record + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&MsgRec, 0, sizeof (MsgRec)); + MsgRec.status = 2; + } + + // Set up control record + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= malloc(sizeof (MsgRec)); + memcpy(MsgHddrPtr[0], &MsgRec, sizeof (MsgRec)); + + LatestMsg=MsgHddrPtr[0]->length; + + NumberofMessages = 0; + + if (MsgRec.status == 1) // Used as file format version + // 0 = original, 1 = Extra email from addr, 2 = More BBS's. + { + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(MsgDatabasePath, Backup1, FALSE); // Copy to .oldformat + + Handle = fopen(MsgDatabasePath, "rb"); + + FileRecsize = sizeof(struct OldMsgInfo); + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + MsgHddrPtr[0]->status = 2; + } + +Next: + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen > 0) + { + // Validate Header + + if (FileRecsize == sizeof(struct MsgInfo)) + { + if (MsgRec.type == 0 || MsgRec.number == 0) + goto Next; + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + // MsgRec.length = strlen(MsgBytes); + free(MsgBytes); + } + else + goto Next; + + Msg = AllocateMsgRecord(); + + memcpy(Msg, &MsgRec, +sizeof (MsgRec)); + } + else + { + // Resizing - record from file is an OldRecInfo + + struct OldMsgInfo * OldMessage = (struct OldMsgInfo *) &MsgRec; + + if (OldMessage->type == 0) + goto Next; + + if (OldMessage->number > 99999 || OldMessage->number < 1) + goto Next; + + Msg = AllocateMsgRecord(); + + + Msg->B2Flags = OldMessage->B2Flags; + memcpy(Msg->bbsfrom, OldMessage->bbsfrom, 7); + memcpy(Msg->bid, OldMessage->bid, 13); + Msg->datechanged = OldMessage->datechanged; + Msg->datecreated = OldMessage->datecreated; + Msg->datereceived = OldMessage->datereceived; + memcpy(Msg->emailfrom, OldMessage->emailfrom, 41); + memcpy(Msg->fbbs , OldMessage->fbbs, 10); + memcpy(Msg->forw , OldMessage->forw, 10); + memcpy(Msg->from, OldMessage->from, 7); + Msg->length = OldMessage->length; + Msg->nntpnum = OldMessage->nntpnum; + Msg->number = OldMessage->number; + Msg->status = OldMessage->status; + memcpy(Msg->title, OldMessage->title, 61); + memcpy(Msg->to, OldMessage->to, 7); + Msg->type = OldMessage->type; + memcpy(Msg->via, OldMessage->via, 41); + } + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + // Move Dates if first run with new format + + if (Msg->datecreated == 0) + Msg->datecreated = Msg->xdatecreated; + + if (Msg->datereceived == 0) + Msg->datereceived = Msg->xdatereceived; + + if (Msg->datechanged == 0) + Msg->datechanged = Msg->xdatechanged; + + BuildNNTPList(Msg); // Build NNTP Groups list + + Msg->Locked = 0; // In case left locked + Msg->Defered = 0; // In case left set. + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + + goto Next; + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + fclose(Handle); +} + +VOID CopyMessageDatabase() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + +// return; + + // Keep 4 Generations + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak"); + + CopyFile(MsgDatabasePath, Backup2, FALSE); // Copy to .bak + +} + +VOID SaveMessageDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + char Key[16]; + struct MsgInfo *Msg; +// char CfgName[MAX_PATH]; + char HEXString1[64]; + char HEXString2[64]; + int n; +// char * CfgBuffer; + char Cfg[1024]; +// int CfgLen = 0; +// FILE * hFile; + +// SaveConfig(ConfigName); // Message Headers now in main config +// return; + +#ifdef LINBPQ + RefreshWebMailIndex(); +#else + if (pRefreshWebMailIndex) + pRefreshWebMailIndex(); +#endif + + Handle = fopen(MsgDatabasePath, "wb"); + + if (Handle == NULL) + { + CriticalErrorHandler("Failed to open message database"); + return; + } + + MsgHddrPtr[0]->status = 2; + MsgHddrPtr[0]->number = NumberofMessages; + MsgHddrPtr[0]->length = LatestMsg; + + for (i=0; i <= NumberofMessages; i++) + { + WriteLen = fwrite(MsgHddrPtr[i], 1, sizeof (struct MsgInfo), Handle); + + if (WriteLen != sizeof(struct MsgInfo)) + { + CriticalErrorHandler("Failed to write message database record"); + return; + } + } + + if (fclose(Handle) != 0) + CriticalErrorHandler("Failed to close message database"); + + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d:\r\n", i); + + n = sprintf(Cfg, "%c|%c|%d|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->number, Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + } + + return; +} + +VOID GetBIDDatabase() +{ + BIDRec BIDRec; + FILE * Handle; + size_t ReadLen; + BIDRecP BID; + int index = 0; + char * ptr, * ptr2; + + // If BID info is in main config file, use it + + group = config_lookup (&cfg, "BIDS"); + + if (group) + { + config_setting_t * entry = config_setting_get_elem (group, index++); + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + while (entry) + { + // entry->name is Bid with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + if (ptr && ptr2) + { + BID = AllocateBIDRecord(); + strcpy(BID->BID, &entry->name[1]); + BID->mode = atoi(ptr); + BID->u.timestamp = atoi(ptr2); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + } + entry = config_setting_get_elem (group, index++); + } + return; + } + + Handle = fopen(BIDDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&BIDRec, 0, sizeof (BIDRec)); + } + + // Set up control record + + BIDRecPtr = malloc(sizeof(void *)); + BIDRecPtr[0] = malloc(sizeof (BIDRec)); + memcpy(BIDRecPtr[0], &BIDRec, sizeof (BIDRec)); + + NumberofBIDs = 0; + +Next: + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen > 0) + { + BID = AllocateBIDRecord(); + memcpy(BID, &BIDRec, sizeof (BIDRec)); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + goto Next; + } + + fclose(Handle); +} + +VOID CopyBIDDatabase() +{ + char Backup[MAX_PATH]; + +// return; + + + strcpy(Backup, BIDDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(BIDDatabasePath, Backup, FALSE); +} + +VOID SaveBIDDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + +// return; // Bids are now in main config and are saved when message is saved + + Handle = fopen(BIDDatabasePath, "wb"); + + BIDRecPtr[0]->u.msgno = NumberofBIDs; // First Record has file size + + for (i=0; i <= NumberofBIDs; i++) + { + WriteLen = fwrite(BIDRecPtr[i], 1, sizeof (BIDRec), Handle); + } + + fclose(Handle); + + return; +} + +BIDRec * LookupBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofBIDs; i++) + { + ptr = BIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) + return ptr; + } + + return NULL; +} + +BIDRec * LookupTempBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofTempBIDs; i++) + { + ptr = TempBIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) return ptr; + } + + return NULL; +} + +VOID RemoveTempBIDS(CIRCUIT * conn) +{ + // Remove any Temp BID records for conn. Called when connection closes - Msgs will be complete or failed + + if (NumberofTempBIDs == 0) + return; + else + { + BIDRec * ptr = NULL; + BIDRec ** NewTempBIDRecPtr = zalloc((NumberofTempBIDs+1) * sizeof(void *)); + int i = 0, n; + + GetSemaphore(&AllocSemaphore, 0); + + for (n = 1; n <= NumberofTempBIDs; n++) + { + ptr = TempBIDRecPtr[n]; + + if (ptr) + { + if (ptr->u.conn == conn) + // Remove this entry + free(ptr); + else + NewTempBIDRecPtr[++i] = ptr; + } + } + + NumberofTempBIDs = i; + + free(TempBIDRecPtr); + + TempBIDRecPtr = NewTempBIDRecPtr; + FreeSemaphore(&AllocSemaphore); + } + +} + +VOID GetBadWordFile() +{ + FILE * Handle; + DWORD FileSize; + char * ptr1, * ptr2; + struct stat STAT; + + if (stat(BadWordsPath, &STAT) == -1) + return; + + FileSize = STAT.st_size; + + Handle = fopen(BadWordsPath, "rb"); + + if (Handle == NULL) + return; + + // Release old info in case a re-read + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + BadWords = NULL; + BadFile = NULL; + NumberofBadWords = 0; + + BadFile = malloc(FileSize+1); + + fread(BadFile, 1, FileSize, Handle); + + fclose(Handle); + + BadFile[FileSize]=0; + + _strlwr(BadFile); // Compares are case-insensitive + + ptr1 = BadFile; + + while (ptr1) + { + if (*ptr1 == '\n') ptr1++; + + ptr2 = strtok_s(NULL, "\r\n", &ptr1); + if (ptr2) + { + if (*ptr2 != '#') + { + BadWords = realloc(BadWords,(++NumberofBadWords+1) * sizeof(void *)); + BadWords[NumberofBadWords] = ptr2; + } + } + else + break; + } +} + +BOOL CheckBadWord(char * Word, char * Msg) +{ + char * ptr1 = Msg, * ptr2; + size_t len = strlen(Word); + + while (*ptr1) // Stop at end + { + ptr2 = strstr(ptr1, Word); + + if (ptr2 == NULL) + return FALSE; // OK + + // Only bad if it ia not part of a longer word + + if ((ptr2 == Msg) || !(isalpha(*(ptr2 - 1)))) // No alpha before + if (!(isalpha(*(ptr2 + len)))) // No alpha after + return TRUE; // Bad word + + // Keep searching + + ptr1 = ptr2 + len; + } + + return FALSE; // OK +} + +BOOL CheckBadWords(char * Msg) +{ + char * dupMsg = _strlwr(_strdup(Msg)); + int i; + + for (i = 1; i <= NumberofBadWords; i++) + { + if (CheckBadWord(BadWords[i], dupMsg)) + { + free(dupMsg); + return TRUE; // Bad + } + } + + free(dupMsg); + return FALSE; // OK + +} + +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertWelcomeMsg, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewWelcomeMsg, LOG_BBS); + else + ExpandAndSendMessage(conn, WelcomeMsg, LOG_BBS); + + if (user->HomeBBS[0] == 0 && !DontNeedHomeBBS) + BBSputs(conn, "Please enter your Home BBS using the Home command.\rYou may also enter your QTH and ZIP/Postcode using qth and zip commands.\r"); + +// if (user->flags & F_Temp_B2_BBS) +// nodeprintf(conn, "%s CMS >\r", BBSName); +// else + SendPrompt(conn, user); +} + +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->Temp->ListSuspended) + return; // Dont send prompt if pausing a listing + + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertPrompt, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewPrompt, LOG_BBS); + else + ExpandAndSendMessage(conn, Prompt, LOG_BBS); + +// if (user->flags & F_Expert) +// nodeprintf(conn, "%s\r", ExpertPrompt); +// else if (conn->NewUser) +// nodeprintf(conn, "%s\r", NewPrompt); +// else +// nodeprintf(conn, "%s\r", Prompt); +} + + + +VOID * _zalloc(size_t len) +{ + // ?? malloc and clear + + void * ptr; + + ptr=malloc(len); + memset(ptr, 0, len); + + return ptr; +} + +BOOL isAMPRMsg(char * Addr) +{ + // See if message is addressed to ampr.org and is either + // for us or we have SendAMPRDirect (ie don't need RMS or SMTP to send it) + + size_t toLen = strlen(Addr); + + if (_memicmp(&Addr[toLen - 8], "ampr.org", 8) == 0) + { + // message is for ampr.org + + char toCall[48]; + char * via; + + strcpy(toCall, _strupr(Addr)); + + via = strlop(toCall, '@'); + + if (_stricmp(via, AMPRDomain) == 0) + { + // message is for us. + + return TRUE; + } + + if (SendAMPRDirect) + { + // We want to send ampr mail direct to host. Queue to BBS AMPR + + if (FindAMPR()) + { + // We have bbs AMPR + + return TRUE; + } + } + } + return FALSE; +} + +struct UserInfo * FindAMPR() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "AMPR") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindRMS() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "RMS") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindBBS(char * Name) +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, Name) == 0) + return bbs; + } + + return NULL; +} + +int CountConnectionsOnPort(int CheckPort) +{ + int n, Count = 0; + CIRCUIT * conn; + int port, sesstype, paclen, maxframe, l4window; + char callsign[11]; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active) + { + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + if (port == CheckPort) + Count++; + } + } + + return Count; +} + + +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type) +{ + char ** Calls; + + if (Type == 'B' && FilterWPBulls && _stricmp(To, "WP") == 0) + return TRUE; + + if (RejFrom && From) + { + Calls = RejFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (RejTo && To) + { + Calls = RejTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (RejAt && ATBBS) + { + Calls = RejAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (RejBID && BID) + { + Calls = RejBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0]) == 0) + return TRUE; + } + + Calls++; + } + } + return FALSE; // Ok to accept +} + +BOOL CheckValidCall(char * From) +{ + unsigned int i; + + if (DontCheckFromCall) + return TRUE; + + if (strcmp(From, "SYSOP") == 0 || strcmp(From, "SYSTEM") == 0 || + strcmp(From, "IMPORT") == 0 || strcmp(From, "SMTP:") == 0 || strcmp(From, "RMS:") == 0) + return TRUE; + + for (i = 1; i < strlen(From); i++) // skip first which may also be digit + { + if (isdigit(From[i])) + { + // Has a digit. Check Last is not digit + + if (isalpha(From[strlen(From) - 1])) + return TRUE; + } + } + + // No digit, return false + + return FALSE; +} + +BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID) +{ + char ** Calls; + + if (HoldFrom && From) + { + Calls = HoldFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldTo && To) + { + Calls = HoldTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldAt && ATBBS) + { + Calls = HoldAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldBID && BID) + { + Calls = HoldBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0]) == 0) + return TRUE; + } + + Calls++; + } + } + return FALSE; // Ok to accept +} + +BOOL CheckifLocalRMSUser(char * FullTo) +{ + struct UserInfo * user = LookupCall(FullTo); + + if (user) + if (user->flags & F_POLLRMS) + return TRUE; + + return FALSE; + +} + + + +int check_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + return (mask[(bbsnumber - 1) / 8] & (1 << ((bbsnumber - 1) % 8))); + else + return 0; +} + + +void set_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] |= (1 << ((bbsnumber - 1) % 8)); +} + + +void clear_fwd_bit (char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] &= (~(1 << ((bbsnumber - 1) % 8))); +} + +VOID BBSputs(CIRCUIT * conn, char * buf) +{ + // Sends to user and logs + + WriteLogLine(conn, '>',buf, (int)strlen(buf) -1, LOG_BBS); + + QueueMsg(conn, buf, (int)strlen(buf)); +} + +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + return; +} + +// nodeprintfEx add a LF if NEEFLF is set + +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + if (conn->BBSFlags & NEEDLF) + QueueMsg(conn, "\r", 1); + + return; +} + + +int compare( const void *arg1, const void *arg2 ); + +VOID SortBBSChain() +{ + struct UserInfo * user; + struct UserInfo * users[161]; + int i = 0, n; + + // Get array of addresses + + for (user = BBSChain; user; user = user->BBSNext) + { + users[i++] = user; + if (i > 160) break; + } + + qsort((void *)users, i, sizeof(void *), compare ); + + BBSChain = NULL; + + // Rechain (backwards, as entries ate put on front of chain) + + for (n = i-1; n >= 0; n--) + { + users[n]->BBSNext = BBSChain; + BBSChain = users[n]; + } +} + +int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + +int CountMessagesTo(struct UserInfo * user, int * Unread) +{ + int i, Msgs = 0; + UCHAR * Call = user->Call; + + *Unread = 0; + + for (i = NumberofMessages; i > 0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (_stricmp(MsgHddrPtr[i]->to, Call) == 0) + { + Msgs++; + if (MsgHddrPtr[i]->status == 'N') + *Unread = *Unread + 1; + } + } + return(Msgs); +} + + + +// Custimised message handling routines. +/* + Variables - a subset of those used by FBB + + $C : Number of the next message. + $I : First name of the connected user. + $L : Number of the latest message. + $N : Number of active messages + $U : Callsign of the connected user. + $W : Inserts a carriage return. + $Z : Last message read by the user (L command). + %X : Number of messages for the user. + %x : Number of new messages for the user. +*/ + +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + size_t len; + char Dollar[] = "$"; + char CR[] = "\r"; + char num[20]; + int Msgs = 0, Unread = 0; + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = ptr - OldP; // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'I': // First name of the connected user. + + pptr = conn->UserPointer->Name; + break; + + case 'L': // Number of the latest message. + + sprintf(num, "%d", LatestMsg); + pptr = num; + break; + + case 'N': // Number of active messages. + + sprintf(num, "%d", NumberofMessages); + pptr = num; + break; + + case 'U': // Callsign of the connected user. + + pptr = conn->UserPointer->Call; + break; + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + case 'Z': // Last message read by the user (L command). + + sprintf(num, "%d", conn->UserPointer->lastmsg); + pptr = num; + break; + + case 'X': // Number of messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + case 'x': // Number of new messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Unread); + pptr = num; + break; + + case 'F': // Number of new messages to forward to this BBS. + + Msgs = CountMessagestoForward(conn->UserPointer); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, (int)strlen(NewMessage)); + + WriteLogLine(conn, '>', NewMessage, (int)len, LOG); + QueueMsg(conn, NewMessage, (int)len); +} + +BOOL isdigits(char * string) +{ + // Returns TRUE id sting is decimal digits + + size_t i, n = strlen(string); + + for (i = 0; i < n; i++) + { + if (isdigit(string[i]) == FALSE) return FALSE; + } + return TRUE; +} + +BOOL wildcardcompare(char * Target, char * Match) +{ + // Do a compare with string *string string* *string* + + // Strings should all be UC + + char Pattern[100]; + char * firststar; + + strcpy(Pattern, Match); + firststar = strchr(Pattern,'*'); + + if (firststar) + { + size_t Len = strlen(Pattern); + + if (Pattern[0] == '*' && Pattern[Len - 1] == '*') // * at start and end + { + Pattern[Len - 1] = 0; + return !(strstr(Target, &Pattern[1]) == NULL); + } + if (Pattern[0] == '*') // * at start + { + // Compare the last len - 1 chars of Target + + size_t Targlen = strlen(Target); + size_t Comparelen = Targlen - (Len - 1); + + if (Len == 1) // Just * + return TRUE; + + if (Comparelen < 0) // Too Short + return FALSE; + + return (memcmp(&Target[Comparelen], &Pattern[1], Len - 1) == 0); + } + + // Must be * at end - compare first Len-1 char + + return (memcmp(Target, Pattern, Len - 1) == 0); + } + + // No WildCards - straight strcmp + return (strcmp(Target, Pattern) == 0); +} + +#ifndef LINBPQ + +PrintMessage(HDC hDC, struct MsgInfo * Msg); + +PrintMessages(HWND hDlg, int Count, int * Indexes) +{ + int i, CurrentMsgIndex; + char MsgnoText[10]; + int Msgno; + struct MsgInfo * Msg; + int Len = MAX_PATH; + BOOL hResult; + PRINTDLG pdx = {0}; + HDC hDC; + +// CHOOSEFONT cf; + LOGFONT lf; + HFONT hFont; + + + // Initialize the PRINTDLG structure. + + pdx.lStructSize = sizeof(PRINTDLG); + pdx.hwndOwner = hWnd; + pdx.hDevMode = NULL; + pdx.hDevNames = NULL; + pdx.hDC = NULL; + pdx.Flags = PD_RETURNDC | PD_COLLATE; + pdx.nMinPage = 1; + pdx.nMaxPage = 1000; + pdx.nCopies = 1; + pdx.hInstance = 0; + pdx.lpPrintTemplateName = NULL; + + // Invoke the Print property sheet. + + hResult = PrintDlg(&pdx); + + memset(&lf, 0, sizeof(LOGFONT)); + + /* + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = pdx.hDC; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_PRINTERFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = PRINTER_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. +*/ + + lf.lfHeight = -56; + lf.lfWeight = 600; + lf.lfOutPrecision = 3; + lf.lfClipPrecision = 2; + lf.lfQuality = 1; + lf.lfPitchAndFamily = '1'; + strcpy (lf.lfFaceName, "Courier New"); + + hFont = CreateFontIndirect(&lf); + + if (hResult) + { + // User clicked the Print button, so use the DC and other information returned in the + // PRINTDLG structure to print the document. + + DOCINFO pdi; + + pdi.cbSize = sizeof(DOCINFO); + pdi.lpszDocName = "BBS Message Print"; + pdi.lpszOutput = NULL; + pdi.lpszDatatype = "RAW"; + pdi.fwType = 0; + + hDC = pdx.hDC; + + SelectObject(hDC, hFont); + + StartDoc(hDC, &pdi); + StartPage(hDC); + + for (i = 0; i < Count; i++) + { + SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Indexes[i], (LPARAM)(LPCTSTR)&MsgnoText); + + Msgno = atoi(MsgnoText); + + for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) + { + Msg = MsgHddrPtr[CurrentMsgIndex]; + + if (Msg->number == Msgno) + { + PrintMessage(hDC, Msg); + break; + } + } + } + + EndDoc(hDC); + } + + if (pdx.hDevMode != NULL) + GlobalFree(pdx.hDevMode); + if (pdx.hDevNames != NULL) + GlobalFree(pdx.hDevNames); + + if (pdx.hDC != NULL) + DeleteDC(pdx.hDC); + + return 0; +} + +PrintMessage(HDC hDC, struct MsgInfo * Msg) +{ + int Len = MAX_PATH; + char * MsgBytes; + char * Save; + int Msglen; + + StartPage(hDC); + + Save = MsgBytes = ReadMessageFile(Msg->number); + + Msglen = Msg->length; + + if (MsgBytes) + { + char Hddr[1000]; + char FullTo[100]; + int HRes, VRes; + char * ptr1, * ptr2; + int LineLen; + + RECT Rect; + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MsgBytes, "Body:"); + if (ptr) + { + Msglen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + MsgBytes = ptr + 4; + } + + HRes = GetDeviceCaps(hDC, HORZRES) - 50; + VRes = GetDeviceCaps(hDC, VERTRES) - 50; + + Rect.top = 50; + Rect.left = 50; + Rect.right = HRes; + Rect.bottom = VRes; + + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_CALCRECT | DT_WORDBREAK); + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_WORDBREAK); + + // process message a line at a time. When page is full, output a page break + + ptr1 = MsgBytes; + ptr2 = ptr1; + + while (Msglen-- > 0) + { + if (*ptr1++ == '\r') + { + // Output this line + + // First check if it will fit + + Rect.top = Rect.bottom; + Rect.right = HRes; + Rect.bottom = VRes; + + LineLen = ptr1 - ptr2 - 1; + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + + if (Rect.bottom >= VRes) + { + EndPage(hDC); + StartPage(hDC); + + Rect.top = 50; + Rect.bottom = VRes; + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + } + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_WORDBREAK); + + if (*(ptr1) == '\n') + { + ptr1++; + Msglen--; + } + + ptr2 = ptr1; + } + } + + free(Save); + + EndPage(hDC); + + } + return 0; +} + +#endif + + +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup) +{ + char FileName[MAX_PATH] = "Messages.in"; + int Files = 0; + int WriteLen=0; + FILE *in; + CIRCUIT dummyconn; + struct UserInfo User; + int Index = 0; + + char Buffer[100000]; + char *buf = Buffer; + + if (FN[0]) // Name supplled + strcpy(FileName, FN); + + else + { +#ifndef LINBPQ + OPENFILENAME Ofn; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = MainWnd; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileName; + Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = BaseDir; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + Ofn.lpstrTitle = NULL;//; + + if (!GetOpenFileName(&Ofn)) + return 0; +#endif + } + + in = fopen(FileName, "rb"); + + if (!(in)) + { + char msg[500]; + sprintf_s(msg, sizeof(msg), "Failed to open %s", FileName); + if (conn) + nodeprintf(conn, "%s\r", msg); +#ifdef WIN32 + else + if (Nopopup == FALSE) + MessageBox(NULL, msg, "BPQMailChat", MB_OK); +#endif + return 0; + } + + memset(&dummyconn, 0, sizeof(CIRCUIT)); + memset(&User, 0, sizeof(struct UserInfo)); + + if (conn == 0) + { + conn = &dummyconn; + + dummyconn.UserPointer = &User; // Was SYSOPCall, but I think that is wrong. + strcpy(User.Call, "IMPORT"); + User.flags |= F_EMAIL; + dummyconn.sysop = TRUE; + dummyconn.BBSFlags = BBS; + + strcpy(dummyconn.Callsign, "IMPORT"); + } + + while(fgets(Buffer, 99999, in)) + { + // First line should start SP/SB ?ST? + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * Msg; + char To[100]= ""; + int msglen; + char * Context; + char * Arg1, * Cmd; + +NextMessage: + + From = NULL; + BID = NULL; + ATBBS = NULL; + To[0]= 0; + + Sleep(100); + + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (Buffer[0] == 0) //Blank Line + continue; + + WriteLogLine(conn, '>', Buffer, (int)strlen(Buffer), LOG_BBS); + + if (dummyconn.sysop == 0) + { + nodeprintf(conn, "%s\r", Buffer); + Flush(conn); + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + fclose(in); + return Files; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + if (dummyconn.sysop) + Debugprintf("Bad Import Line %s", Buffer); + else + nodeprintf(conn, "Bad Import Line %s\r", Buffer); + + fclose(in); + return Files; + } + + strcpy(To, Arg1); + + if (DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + { + if (CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL)) + { + Msg = conn->TempMsg; + + // SP is Ok, read message; + + ClearQueue(conn); + + fgets(Buffer, 99999, in); + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + if (strlen(Buffer) > 60) + Buffer[60] = 0; + + strcpy(Msg->title, Buffer); + + // Read the lines + + conn->Flags |= GETTINGMESSAGE; + + Buffer[0] = 0; + + fgets(Buffer, 99999, in); + + while ((conn->Flags & GETTINGMESSAGE) && Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + msglen = (int)strlen(Buffer); + Buffer[msglen++] = 13; + ProcessMsgLine(conn, conn->UserPointer,Buffer, msglen); + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + + // Message completed (or off end of file) + + Files ++; + + ClearQueue(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + else + { + fclose(in); + return Files; + } + } + else + { + // Create failed + + Flush(conn); + } + } + + // Search for next message + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + while (Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (_stricmp(Buffer, "/EX") == 0) + { + // Found end + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + } + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + } + + fclose(in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + return Files; +} +char * ReadMessageFileEx(struct MsgInfo * MsgRec) +{ + // Sets Message Size from File Size + + int msgno = MsgRec->number; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + MsgRec->length = FileSize; + + return MsgBytes; +} + +char * ReadMessageFile(int msgno) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes = malloc(FileSize + 100); // A bit of space for alias substitution on B2 + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + + return MsgBytes; +} + + +int QueueMsg(ConnectionInfo * conn, char * msg, int len) +{ + // Add Message to queue for this connection + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // Create or extend buffer + + GetSemaphore(&OutputSEM, 0); + + conn->OutputQueue=realloc(conn->OutputQueue, conn->OutputQueueLength + len); + + if (conn->OutputQueue == NULL) + { + // relloc failed - should never happen, but clean up + + CriticalErrorHandler("realloc failed to expand output queue"); + FreeSemaphore(&OutputSEM); + return 0; + } + + memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); + conn->OutputQueueLength += len; + FreeSemaphore(&OutputSEM); + + return len; +} + +void TrytoSend() +{ + // call Flush on any connected streams with queued data + + ConnectionInfo * conn; + struct ConsoleInfo * Cons; + + int n; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + Flush(conn); + + // if an FLARQ mail has been sent see if queues have cleared + + if (conn->BBSFlags & YAPPTX) + { + YAPPSendData(conn); + } + else if (conn->OutputQueue == NULL && (conn->BBSFlags & ARQMAILACK)) + { + int n = TXCount(conn->BPQStream); // All Sent and Acked? + + if (n == 0) + { + struct MsgInfo * Msg = conn->FwdMsg; + + conn->ARQClearCount--; + + if (conn->ARQClearCount <= 0) + { + Logprintf(LOG_BBS, conn, '>', "ARQ Send Complete"); + + // Mark mail as sent, and look for more + + clear_fwd_bit(Msg->fbbs, conn->UserPointer->BBSNumber); + set_fwd_bit(Msg->forw, conn->UserPointer->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) + { + Msg->status = 'F'; // Mark as forwarded + Msg->datechanged=time(NULL); + } + + conn->BBSFlags &= ~ARQMAILACK; + conn->UserPointer->ForwardingInfo->MsgCount--; + + SaveMessageDatabase(); + SendARQMail(conn); // See if any more - close if not + } + } + else + conn->ARQClearCount = 10; + } + } + } +#ifndef LINBPQ + for (Cons = ConsHeader[0]; Cons; Cons = Cons->next) + { + if (Cons->Console) + Flush(Cons->Console); + } +#endif +} + + +void Flush(CIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + if (conn->OutputQueue == NULL) + { + // Nothing to send. If Close after Flush is set, disconnect + + if (conn->CloseAfterFlush) + { + conn->CloseAfterFlush--; + + if (conn->CloseAfterFlush) + return; + + Disconnect(conn->BPQStream); + conn->ErrorCount = 0; + } + + return; // Nothing to send + } + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent=0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 15) + return; // Busy + + if (conn->BBSFlags & SYSOPCHAT) // Suspend queued output while sysop chatting + return; + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + return; + + if (tosend <= conn->paclen) + len=tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + if (conn->Paging) + { + // look for CR chars in message to send. Increment LinesSent, and stop if at limit + + UCHAR * ptr1 = &conn->OutputQueue[conn->OutputGetPointer]; + UCHAR * ptr2; + int lenleft = len; + + ptr2 = memchr(ptr1, 0x0d, len); + + while (ptr2) + { + conn->LinesSent++; + ptr2++; + lenleft = len - (int)(ptr2 - ptr1); + + if (conn->LinesSent >= conn->PageLen) + { + len = (int)(ptr2 - &conn->OutputQueue[conn->OutputGetPointer]); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + conn->OutputGetPointer+=len; + tosend-=len; + SendUnbuffered(conn->BPQStream, "bort, Continue..>", 25); + FreeSemaphore(&OutputSEM); + return; + + } + ptr2 = memchr(ptr2, 0x0d, lenleft); + } + } + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer+=len; + + FreeSemaphore(&OutputSEM); + + tosend-=len; + sent++; + + if (sent > 15) + return; + } + + // All Sent. Free buffers and reset pointers + + conn->LinesSent = 0; + + ClearQueue(conn); +} + +VOID ClearQueue(ConnectionInfo * conn) +{ + if (conn->OutputQueue == NULL) + return; + + GetSemaphore(&OutputSEM, 0); + + free(conn->OutputQueue); + + conn->OutputQueue=NULL; + conn->OutputGetPointer=0; + conn->OutputQueueLength=0; + + FreeSemaphore(&OutputSEM); +} + + + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB) +{ + struct UserInfo * user; + + Msg->status='K'; + Msg->datechanged=time(NULL); + + // Remove any forwarding references + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount--; + clear_fwd_bit(Msg->fbbs, user->BBSNumber); + } + } + } + if (SaveDB) + SaveMessageDatabase(); + RebuildNNTPList(); +} + +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + struct MsgInfo * Msg; + + while (Arg1) + { + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + goto Next; + } + + if (Msg->type != 'T') + { + nodeprintf(conn, "Message %d not an NTS Message\r", msgno); + goto Next; + } + + if (Msg->status == 'N') + nodeprintf(conn, "Warning - Message has status N\r"); + + Msg->status = 'D'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + + nodeprintf(conn, "Message #%d Flagged as Delivered\r", msgno); + Next: + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + // Param is either ALL or a list of numbers + + if (Arg1 == NULL) + { + nodeprintf(conn, "No message number\r"); + return; + } + + if (_stricmp(Arg1, "ALL") == 0) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", Msg->number); + } + } + return; + } + + while (Arg1) + { + msgno = atoi(Arg1); + Msg = GetMsgFromNumber(msgno); + + if (Msg) + { + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", msgno); + } + else + { + nodeprintf(conn, "Message #%d was not held\r", msgno); + } + } + else + nodeprintf(conn, "Message #%d not found\r", msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoKillCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + switch (toupper(Cmd[1])) + { + + case 0: // Just K + + while (Arg1) + { + msgno = atoi(Arg1); + KillMessage(conn, user, msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + SaveMessageDatabase(); + return; + + case 'M': // Kill Mine + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + { + if (Msg->type == 'P' && Msg->status == 'Y') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + + SaveMessageDatabase(); + return; + + case 'H': // Kill Held + + if (conn->sysop) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + SaveMessageDatabase(); + return; + + case '>': // K> - Kill to + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesTo(conn, user, Arg1) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + } + + case '<': + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesFrom(conn, user, Arg1) == 0); + BBSputs(conn, "No Messages found\r"); + + return; + } + } + + nodeprintf(conn, "*** Error: Invalid Kill option %c\r", Cmd[1]); + + return; + +} + +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->to, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->from, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg) +{ + if (SYSOP || (Msg->type == 'T' && UserCantKillT == FALSE)) + return TRUE; + + if (Msg->type == 'P') + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + + if (Msg->type == 'B') + if (_stricmp(Msg->from, Call) == 0) + return TRUE; + + return FALSE; +} + +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL || Msg->status == 'K') + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (OkToKillMessage(conn->sysop, user->Call, Msg)) + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", msgno); + } + else + nodeprintf(conn, "Not your message\r"); +} + + +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp) +{ + char FullFrom[80]; + char FullTo[80]; + + strcpy(FullFrom, Msg->from); + + if ((_stricmp(Msg->from, "RMS:") == 0) || (_stricmp(Msg->from, "SMTP:") == 0) || + Temp->SendFullFrom || (_stricmp(Msg->emailfrom, "@winlink.org") == 0)) + strcat(FullFrom, Msg->emailfrom); + + if (_stricmp(Msg->to, "RMS") == 0) + { + sprintf(FullTo, "RMS:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + else + + if (Msg->to[0] == 0 && Msg->via[0] != 0) + { + sprintf(FullTo, "smtp:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + + else + if (Msg->via[0] != 0) + { + char Via[80]; + strcpy(Via, Msg->via); + strlop(Via, '.'); // Only show first part of via + nodeprintf(conn, "%-6d %s %c%c %5d %-7s@%-6s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, Via, FullFrom, Msg->title); + } + else + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, FullFrom, Msg->title); + + // if paging, stop two before page lengh. This lets us send the continue prompt, save status + // and exit without triggering the system paging code. We can then read a message then resume listing + + if (Temp->ListActive && conn->Paging) + { + Temp->LinesSent++; + + if ((Temp->LinesSent + 1) >= conn->PageLen) + { + nodeprintf(conn, "bort, , = Continue..>"); + Temp->LastListedInPagedMode = Msg->number; + Temp->ListSuspended = TRUE; + return TRUE; + } + } + + return FALSE; +} + +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context) +{ + struct TempUserInfo * Temp = user->Temp; + struct MsgInfo * Msg; + + // Allow compound selection, eg LTN or LFP + + // types P N T + // Options LL LR L< L> L@ LM LC (L* used internally for just L, ie List New + // Status N Y H F K D + + // Allowing options in any order complicates paging. May be best to parse options once and restore if paging. + + Temp->ListActive = TRUE; + Temp->LinesSent = 0; + + if (Resuming) + { + // Entered after a paging pause. Selection fields are already set up + + // We have reentered list command after a pause. The next message to list is in Temp->LastListedInPagedMode + +// Start = Temp->LastListedInPagedMode; + Temp->ListSuspended = FALSE; + } + else + { + Temp->ListRangeEnd = LatestMsg; + Temp->ListRangeStart = 1; + Temp->LLCount = 0; + Temp->SendFullFrom = 0; + Temp->ListType = 0; + Temp->ListStatus = 0; + Temp->ListSelector = 0; + Temp->UpdateLatest = 0; + Temp->LastListParams[0] = 0; + Temp->IncludeKilled = 1; // SYSOP include Killed except LM + + //Analyse L params. + + _strupr(Cmd); + + if (strcmp(Cmd, "LC") == 0) // List Bull Categories + { + ListCategories(conn); + return; + } + + // if command is just L or LR start from last listed + + if (Arg1 == NULL) + { + if (strcmp(Cmd, "L") == 0 || strcmp(Cmd, "LR") == 0) + { + if (LatestMsg == conn->lastmsg) + { + BBSputs(conn, "No New Messages\r"); + return; + } + + Temp->UpdateLatest = 1; + Temp->ListRangeStart = conn->lastmsg; + } + } + + if (strchr(Cmd, 'V')) // Verbose + Temp->SendFullFrom = 'V'; + + if (strchr(Cmd, 'R')) + Temp->ListDirn = 'R'; + else + Temp->ListDirn = '*'; // Default newest first + + Cmd++; // skip L + + if (strchr(Cmd, 'T')) + Temp->ListType = 'T'; + else if (strchr(Cmd, 'P')) + Temp->ListType = 'P'; + else if (strchr(Cmd, 'B')) + Temp->ListType = 'B'; + + if (strchr(Cmd, 'N')) + Temp->ListStatus = 'N'; + else if (strchr(Cmd, 'Y')) + Temp->ListStatus = 'Y'; + else if (strchr(Cmd, 'F')) + Temp->ListStatus = 'F'; + else if (strchr(Cmd, '$')) + Temp->ListStatus = '$'; + else if (strchr(Cmd, 'H')) + Temp->ListStatus = 'H'; + else if (strchr(Cmd, 'K')) + Temp->ListStatus = 'K'; + else if (strchr(Cmd, 'D')) + Temp->ListStatus = 'D'; + + // H or K only by Sysop + + switch (Temp->ListStatus) + { + case 'K': + case 'H': // List Status + + if (conn->sysop) + break; + + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + return; + } + + if (strchr(Cmd, '<')) + Temp->ListSelector = '<'; + else if (strchr(Cmd, '>')) + Temp->ListSelector = '>'; + else if (strchr(Cmd, '@')) + Temp->ListSelector = '@'; + else if (strchr(Cmd, 'M')) + { + Temp->ListSelector = 'M'; + Temp->IncludeKilled = FALSE; + } + + // Param could be single number, number range or call + + if (Arg1) + { + if (strchr(Cmd, 'L')) // List Last + { + // Param is number + + if (Arg1) + Temp->LLCount = atoi(Arg1); + } + else + { + // Range nnn-nnn or single value or callsign + + char * Arg2, * Arg3, * Range; + char seps[] = " \t\r"; + UINT From=LatestMsg, To=0; + + Arg2 = strtok_s(NULL, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Temp->ListSelector && Temp->ListSelector != 'M') + { + // < > or @ - first param is callsign + + strcpy(Temp->LastListParams, Arg1); + + // Just possible number range + + Arg1 = Arg2; + Arg2 = Arg3; + Arg3 = strtok_s(NULL, seps, &Context); + } + + if (Arg1) + { + Range = strchr(Arg1, '-'); + + // A number could be a Numeric Bull Dest (eg 44) + // I think this can only resaonably be > + + if (isdigits(Arg1)) + To = From = atoi(Arg1); + + if (Arg2) + From = atoi(Arg2); + else + { + if (Range) + { + Arg3 = strlop(Arg1, '-'); + + To = atoi(Arg1); + + if (Arg3 && Arg3[0]) + From = atoi(Arg3); + else + From = LatestMsg; + } + } + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + Temp->ListRangeStart = To; + Temp->ListRangeEnd = From; + } + } + } + } + + // Run through all messages (either forwards or backwards) and list any that match all selection criteria + + while (1) + { + if (Temp->ListDirn == 'R') + Msg = GetMsgFromNumber(Temp->ListRangeStart); + else + Msg = GetMsgFromNumber(Temp->ListRangeEnd); + + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop, Temp->IncludeKilled)) // Check if user is allowed to list this message + { + // Check filters + + if (Temp->ListStatus && Temp->ListStatus != Msg->status) + goto skip; + + if (Temp->ListType && Temp->ListType != Msg->type) + goto skip; + + if (Temp->ListSelector == '<') + if (_stricmp(Msg->from, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '>') + if (_stricmp(Msg->to, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '@') + if (_memicmp(Msg->via, Temp->LastListParams, strlen(Temp->LastListParams)) != 0 && + (_stricmp(Temp->LastListParams, "SMTP:") != 0 || Msg->to[0] != 0)) + goto skip; + + if (Temp->ListSelector == 'M') + if (_stricmp(Msg->to, user->Call) != 0 && + (_stricmp(Msg->to, "SYSOP") != 0 || ((user->flags & F_SYSOP_IN_LM) == 0))) + + goto skip; + + if (ListMessage(Msg, conn, Temp)) + { + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + return; // Hit page limit + } + + if (Temp->LLCount) + { + Temp->LLCount--; + if (Temp->LLCount == 0) + return; // LL count reached + } +skip:; + } + + if (Temp->ListRangeStart == Temp->ListRangeEnd) + { + // if using L or LR (list new) update last listed field + + if (Temp->UpdateLatest) + conn->lastmsg = LatestMsg; + + return; + } + + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + if (Temp->ListRangeStart > 100000 || Temp->ListRangeEnd < 0) // Loop protection! + return; + + } + +/* + + switch (Cmd[0]) + { + + case '*': // Just L + case 'R': // LR = List Reverse + + if (Arg1) + { + // Range nnn-nnn or single value + + char * Arg2, * Arg3; + char * Context; + char seps[] = " -\t\r"; + UINT From=LatestMsg, To=0; + char * Range = strchr(Arg1, '-'); + + Arg2 = strtok_s(Arg1, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Arg2) + To = From = atoi(Arg2); + + if (Arg3) + From = atoi(Arg3); + else + if (Range) + From = LatestMsg; + + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + if (Cmd[1] == 'R') + { + if (Start) + To = Start + 1; + + ListMessagesInRangeForwards(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + else + { + if (Start) + From = Start - 1; + + ListMessagesInRange(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + } + else + + if (LatestMsg == conn->lastmsg) + BBSputs(conn, "No New Messages\r"); + else if (Cmd[1] == 'R') + ListMessagesInRangeForwards(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + else + ListMessagesInRange(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + + conn->lastmsg = LatestMsg; + + return; + + + case 'L': // List Last + + if (Arg1) + { + int i = atoi(Arg1); + int m = NumberofMessages; + + if (Resuming) + i = Temp->LLCount; + else + Temp->LLCount = i; + + for (; i>0 && m != 0; i--) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + i++; + continue; + } + + Temp->LLCount--; + + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + } + return; + + case 'M': // LM - List Mine + + if (ListMessagesTo(conn, user, user->Call, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + return; + + case '>': // L> - List to + + if (Arg1) + if (ListMessagesTo(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + + return; + + case '<': + + if (Arg1) + if (ListMessagesFrom(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case '@': + + if (Arg1) + if (ListMessagesAT(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case 'N': + case 'Y': + case 'F': + case '$': + case 'D': // Delivered NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (Temp->ListType) + { + if (MsgHddrPtr[m]->status == Cmd[1] && MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + else + { + if (MsgHddrPtr[m]->status == toupper(Cmd[1])) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + m--; + } + } + } + return; + + case 'K': + case 'H': // List Status + + if (conn->sysop) + { + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (MsgHddrPtr[i]->status == toupper(Cmd[1])) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, SendFullFrom)) + return; // Hit page limit + + } + } + + if (Msgs == 0) + BBSputs(conn, "No Messages found\r"); + } + else + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + + return; + + case 'C': + { + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; + Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; + } + } + + // Could be P B or T if specified without a status + + switch (Temp->ListType) + { + case 'P': + case 'B': + case 'T': // NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + + return; + } + } + +*/ + nodeprintf(conn, "*** Error: Invalid List option %c\r", Cmd[1]); + +} + +void ListCategories(ConnectionInfo * conn) +{ + // list bull categories + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; +Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; +} + +/* +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if ((_stricmp(MsgHddrPtr[i]->to, Call) == 0) || + ((conn->sysop) && _stricmp(Call, SYSOPCall) == 0 && + _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0 && (user->flags & F_SYSOP_IN_LM))) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} + +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_stricmp(MsgHddrPtr[i]->from, Call) == 0) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + return Msgs; // Hit page limit + + } + } + + return(Msgs); +} + +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom,int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_memicmp(MsgHddrPtr[i]->via, Call, strlen(Call)) == 0 || + (_stricmp(Call, "SMTP:") == 0 && MsgHddrPtr[i]->to[0] == 0)) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} +*/ +int GetUserMsg(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'K') + { + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m--; + + } while (m> 0); + + return 0; +} + + +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled) +{ + // Return TRUE if user is allowed to read message + + if (Msg->status == 'K' && IncludeKilled == 0) + return FALSE; + + if (SYSOP) + return TRUE; // Sysop can list or read anything + + if ((Msg->status != 'K') && (Msg->status != 'H')) + { + if (Msg->type == 'B' || Msg->type == 'T') return TRUE; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + } + } + + return FALSE; +} +/* +int GetUserMsgForwards(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->status != 'K') + { + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m++; + + } while (m <= NumberofMessages); + + return 0; + +} + + +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m >= End; m--) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + + } +} + + +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int End, int Start, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m <= End; m++) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + } +} +*/ + +void DoReadCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + + switch (toupper(Cmd[1])) + { + case 0: // Just R + + while (Arg1) + { + msgno = atoi(Arg1); + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + ReadMessage(conn, user, msgno); + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; + + case 'M': // Read Mine (Unread Messages) + + if (toupper(Cmd[2]) == 'R') + { + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + else + { + for (i = NumberofMessages; i > 0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + + return; + } + + nodeprintf(conn, "*** Error: Invalid Read option %c\r", Cmd[1]); + + return; +} + +int RemoveLF(char * Message, int len) +{ + // Remove lf chars and nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + if (*ptr1 == '\r') + if (*(ptr1+1) == '\n') + { + ptr1++; + len--; + } + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + + + +int RemoveNulls(char * Message, int len) +{ + // Remove nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + char * MsgBytes, * Save; + char FullTo[100]; + int Index = 0; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (!CheckUserMsg(Msg, user->Call, conn->sysop, TRUE)) + { + nodeprintf(conn, "Message %d not for you\r", msgno); + return; + } + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + nodeprintf(conn, "From: %s%s\rTo: %s\rType/Status: %c%c\rDate/Time: %s\rBid: %s\rTitle: %s\r\r", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + MsgBytes = Save = ReadMessageFile(msgno); + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + if (MsgBytes) + { + int Length = Msg->length; + + if (Msg->B2Flags & B2Msg) + { + char * ptr; + + // if message has attachments, display them if plain text + + if (Msg->B2Flags & Attachments) + { + char * FileName[100]; + int FileLen[100]; + int Files = 0; + int BodyLen, NewLen; + int i; + char *ptr2; + char Msg[512]; + int Len; + + ptr = MsgBytes; + + Len = sprintf(Msg, "Message has Attachments\r\r"); + QueueMsg(conn, Msg, Len); + + while(*ptr != 13) + { + ptr2 = strchr(ptr, 10); // Find CR + + if (memcmp(ptr, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr[6]); + } + + if (memcmp(ptr, "File: ", 6) == 0) + { + char * ptr1 = strchr(&ptr[6], ' '); // Find Space + + FileLen[Files] = atoi(&ptr[6]); + + FileName[Files++] = &ptr1[1]; + *(ptr2 - 1) = 0; + } + + ptr = ptr2; + ptr++; + } + + ptr += 2; // Over Blank Line and Separator + + NewLen = RemoveLF(ptr, BodyLen); + + QueueMsg(conn, ptr, NewLen); // Display Body + + ptr += BodyLen + 2; // to first file + + for (i = 0; i < Files; i++) + { + char Msg[512]; + int Len, n; + char * p = ptr; + char c; + + // Check if message is probably binary + + int BinCount = 0; + + NewLen = RemoveLF(ptr, FileLen[i]); // Removes LF agter CR but not on its own + + for (n = 0; n < NewLen; n++) + { + c = *p; + + if (c == 10) + *p = 13; + + if (c==0 || (c & 128)) + BinCount++; + + p++; + + } + + if (BinCount > NewLen/10) + { + // File is probably Binary + + Len = sprintf(Msg, "\rAttachment %s is a binary file\r", FileName[i]); + QueueMsg(conn, Msg, Len); + } + else + { + Len = sprintf(Msg, "\rAttachment %s\r\r", FileName[i]); + QueueMsg(conn, Msg, Len); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += NewLen; + + QueueMsg(conn, ptr, NewLen); + } + + ptr += FileLen[i]; + ptr +=2; // Over separator + } + goto sendEOM; + } + + // Remove B2 Headers (up to the File: Line) + + ptr = strstr(MsgBytes, "Body:"); + + if (ptr) + { + MsgBytes = ptr; + Length = (int)strlen(ptr); + } + } + + // Remove lf chars + + Length = RemoveLF(MsgBytes, Length); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += Length; + + QueueMsg(conn, MsgBytes, Length); + +sendEOM: + + free(Save); + + nodeprintf(conn, "\r\r[End of Message #%d from %s%s]\r", msgno, Msg->from, Msg->emailfrom); + + if ((_stricmp(Msg->to, user->Call) == 0) || ((conn->sysop) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + } + } + } + } + else + { + nodeprintf(conn, "File for Message %d not found\r", msgno); + } +} + struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + m = GetUserMsg(m, Call, sysop); + + if (m == 0) + return NULL; + + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + m--; + + } while (m> 0); + + return NULL; + +} + + +char * ReadInfoFile(char * File) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + char * ptr1 = 0, * ptr2; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BaseDir, File); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize] = 0; + + ptr1 = MsgBytes; + + // Replace LF or CRLF with CR + + // First remove cr from crlf + + while(ptr2 = strstr(ptr1, "\r\n")) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + } + + // Now replace lf with cr + + ptr1 = MsgBytes; + + while (*ptr1) + { + if (*ptr1 == '\n') + *(ptr1) = '\r'; + + ptr1++; + } + + return MsgBytes; +} + +char * FormatDateAndTime(time_t Datim, BOOL DateOnly) +{ + struct tm *tm; + static char Date[]="xx-xxx hh:mmZ"; + + tm = gmtime(&Datim); + + if (tm) + sprintf_s(Date, sizeof(Date), "%02d-%3s %02d:%02dZ", + tm->tm_mday, month[tm->tm_mon], tm->tm_hour, tm->tm_min); + + if (DateOnly) + { + Date[6]=0; + return Date; + } + + return Date; +} + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); + + +BOOL DoSendCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * OldMsg; + char OldTitle[62]; + char NewTitle[62]; + char To[100]= ""; + int msgno; + + if (Cmd[1] == 0) Cmd[1] ='P'; // Just S means SP + + switch (toupper(Cmd[1])) + { + case 'B': + + if (RefuseBulls) + { + nodeprintf(conn, "*** Error: This system doesn't allow sending Bulls\r"); + return FALSE; + } + + if (user->flags & F_NOBULLS) + { + nodeprintf(conn, "*** Error: You are not allowed to send Bulls\r"); + return FALSE; + } + + + case 'P': + case 'T': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + return CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL); + + case 'R': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + Arg1=&OldMsg->from[0]; + + strcpy(To, Arg1); + + if (_stricmp(Arg1, "SMTP:") == 0 || _stricmp(Arg1, "RMS:") == 0 || OldMsg->emailfrom) + { + // SMTP message. Need to get the real sender from the message + + sprintf(To, "%s%s", Arg1, OldMsg->emailfrom); + } + + if (!DecodeSendParams(conn, "", &From, To, &ATBBS, &BID)) + return FALSE; + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 57) OldTitle[57] = 0; + + strcpy(NewTitle, "Re:"); + strcat(NewTitle, OldTitle); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + + return TRUE; + + case 'C': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 56) OldTitle[56] = 0; + + strcpy(NewTitle, "Fwd:"); + strcat(NewTitle, OldTitle); + + conn->CopyBuffer = ReadMessageFile(msgno); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + } + + + nodeprintf(conn, "*** Error: Invalid Send option %c\r", Cmd[1]); + + return FALSE; +} + +char * CheckToAddress(CIRCUIT * conn, char * Addr) +{ + // Check one element of Multiple Address + + if (conn == NULL || !(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(Addr, "SYSOP") == 0) + { + return _strdup(Addr); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(Addr, BBSName) == 0) + return _strdup(SYSOPCall); + + + if (strchr(Addr, '@') == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + char * NewAddr = malloc(250); + if (conn) + nodeprintf(conn, "Address %s - @%s added from HomeBBS\r", Addr, ToUser->HomeBBS); + sprintf(NewAddr, "%s@%s", Addr, ToUser->HomeBBS); + return NewAddr; + } + } + else + { + WPRecP WP = LookupWP(Addr); + + if (WP) + { + char * NewAddr = malloc(250); + + if (conn) + nodeprintf(conn, "Address %s - @%s added from WP\r", Addr, WP->first_homebbs); + sprintf(NewAddr, "%s@%s", Addr, WP->first_homebbs); + return NewAddr; + } + } + } + } + + // Check SMTP and RMS Addresses + + if ((_memicmp(Addr, "rms:", 4) == 0) || (_memicmp(Addr, "rms/", 4) == 0)) + { + Addr[3] = ':'; // Replace RMS/ with RMS: + + if (conn && !FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + } + else if ((_memicmp(Addr, "smtp:", 5) == 0) || (_memicmp(Addr, "smtp/", 5) == 0)) + { + Addr[4] = ':'; // Replace smpt/ with smtp: + + if (ISP_Gateway_Enabled) + { + if (conn && (conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + } + else + { + if (conn) + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + + return _strdup(Addr); +} + + +char Winlink[] = "WINLINK.ORG"; + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char *To, char ** ATBBS, char ** BID) +{ + char * ptr; + char seps[] = " \t\r"; + WPRecP WP; + char * ToCopy = _strdup(To); + int Len; + + conn->ToCount = 0; + + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + // Having trailing ; will mess up parsing multiple addresses, so remove. + + while (To[strlen(To) - 1] == ';') + To[strlen(To) - 1] = 0; + + if (strchr(Context, ';') || strchr(To, ';')) + { + // Multiple Addresses - put address list back together + + char * p; + + To[strlen(To)] = ' '; + Context = To; + + while (p = strchr(Context, ';')) + { + // Multiple Addressees + + To = strtok_s(NULL, ";", &Context); + Len = (int)strlen(To); + conn->To = realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + + To = strtok_s(NULL, seps, &Context); + + Len = (int)strlen(To); + conn->To=realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + else + { + // Single Call + + // accept CALL!CALL for source routed message + + if (strchr(To, '@') == 0 && strchr(To, '!')) // Bang route without @ + { + char * bang = strchr(To, '!'); + + memmove(bang + 1, bang, strlen(bang)); // Move !call down one + + *ATBBS = strlop(To, '!');; + } + + // Accept call@call (without spaces) - but check for smtp addresses + + if (_memicmp(To, "smtp:", 5) != 0 && _memicmp(To, "rms:", 4) != 0 && _memicmp(To, "rms/", 4) != 0) + { + ptr = strchr(To, '@'); + + if (ptr) + { + // If looks like a valid email address, treat as such + + int tolen; + *ATBBS = strlop(To, '@'); + + strlop(To, '-'); // Cant have SSID on BBS Name + + tolen = (int)strlen(To); + + if (tolen > 6 || !CheckifPacket(*ATBBS)) + { + // Probably Email address. Add smtp: or rms: + + if (FindRMS() || strchr(*ATBBS, '!')) // have RMS or source route + sprintf(To, "rms:%s", ToCopy); + else if (ISP_Gateway_Enabled) + sprintf(To, "smtp:%s", ToCopy); + else if (isAMPRMsg(ToCopy)) + sprintf(To, "rms:%s", ToCopy); + + } + } + } + } + + free(ToCopy); + + // Look for Optional fields; + + ptr = strtok_s(NULL, seps, &Context); + + while (ptr) + { + if (strcmp(ptr, "@") == 0) + { + *ATBBS = _strupr(strtok_s(NULL, seps, &Context)); + } + else if(strcmp(ptr, "<") == 0) + { + *From = strtok_s(NULL, seps, &Context); + } + else if (ptr[0] == '$') + *BID = &ptr[1]; + else + { + nodeprintf(conn, "*** Error: Invalid Format\r"); + return FALSE; + } + ptr = strtok_s(NULL, seps, &Context); + } + + // Only allow < from a BBS + + if (*From) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "*** < can only be used by a BBS\r"); + return FALSE; + } + } + + if (!*From) + *From = conn->UserPointer->Call; + + if (!(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(To, "SYSOP") == 0) + { + conn->LocalMsg = TRUE; + return TRUE; + } + + if (!*ATBBS && conn->ToCount == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(To); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + *ATBBS = Winlink; + nodeprintf(conn, "Redirecting to winlink.org\r", *ATBBS); + } + else if (ToUser->HomeBBS[0]) + { + *ATBBS = ToUser->HomeBBS; + nodeprintf(conn, "Address @%s added from HomeBBS\r", *ATBBS); + } + else + { + conn->LocalMsg = TRUE; + } + } + else + { + conn->LocalMsg = FALSE; + WP = LookupWP(To); + + if (WP) + { + *ATBBS = WP->first_homebbs; + nodeprintf(conn, "Address @%s added from WP\r", *ATBBS); + } + } + } + } + return TRUE; +} + +BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title) +{ + struct MsgInfo * Msg, * TestMsg; + char * via = NULL; + char * FromHA; + + // Create a temp msg header entry + + if (conn->ToCount) + { + } + else + { + if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType)) + { + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - REJECTED\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error - Message Filters prevent sending this message\r"); + + return FALSE; + } + } + + Msg = malloc(sizeof (struct MsgInfo)); + + if (Msg == 0) + { + CriticalErrorHandler("malloc failed for new message header"); + return FALSE; + } + + memset(Msg, 0, sizeof (struct MsgInfo)); + + conn->TempMsg = Msg; + + Msg->type = MsgType; + + if (conn->UserPointer->flags & F_HOLDMAIL) + Msg->status = 'H'; + else + Msg->status = 'N'; + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (BID) + { + BIDRec * TempBID; + + // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message + // If we do, reject it. If not, accept it again. (do we need some loop protection ???) + + TempBID = LookupBID(BID); + + if (TempBID) + { + if (MsgType == 'B') + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + + TestMsg = GetMsgFromNumber(TempBID->u.msgno); + + // if the same TO we will assume the same message + + if (TestMsg && strcmp(TestMsg->to, ToCall) == 0) + { + // We have this message. If we have already forwarded it, we should accept it again + + if ((TestMsg->status == 'N') || (TestMsg->status == 'Y')|| (TestMsg->status == 'H')) + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + } + } + + if (strlen(BID) > 12) BID[12] = 0; + strcpy(Msg->bid, BID); + + // Save BID in temp list in case we are offered it again before completion + + TempBID = AllocateTempBIDRecord(); + strcpy(TempBID->BID, BID); + TempBID->u.conn = conn; + + } + + if (conn->ToCount) + { + } + else + { + if (_memicmp(ToCall, "rms:", 4) == 0) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + + via=strlop(ToCall, ':'); + _strupr(ToCall); + } + else if (_memicmp(ToCall, "rms/", 4) == 0) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + + via=strlop(ToCall, '/'); + _strupr(ToCall); + } + else if (_memicmp(ToCall, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + if ((conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + via=strlop(ToCall, ':'); + ToCall[0] = 0; + } + else + { + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + else + { + _strupr(ToCall); + if (ATBBS) + via=_strupr(ATBBS); + } + + strlop(ToCall, '-'); // Remove any (illegal) ssid + if (strlen(ToCall) > 6) ToCall[6] = 0; + + strcpy(Msg->to, ToCall); + + if (SendBBStoSYSOPCall) + if (_stricmp(ToCall, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (via) + { + if (strlen(via) > 40) via[40] = 0; + + strcpy(Msg->via, via); + } + + } // End of Multiple Dests + + // Look for HA in From (even if we shouldn't be getting it!) + + FromHA = strlop(From, '@'); + + + strlop(From, '-'); // Remove any (illegal) ssid + if (strlen(From) > 6) From[6] = 0; + strcpy(Msg->from, From); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + if (Title) // Only used by SR and SC + { + strcpy(Msg->title, Title); + conn->Flags |= GETTINGMESSAGE; + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + return TRUE; + } + + if (conn->BBSFlags & FLARQMODE) + return TRUE; + + if (!(conn->BBSFlags & FBBCompressed)) + conn->Flags |= GETTINGTITLE; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "Enter Title (only):\r"); + else + if (!(conn->BBSFlags & FBBForwarding)) + nodeprintf(conn, "OK\r"); + + return TRUE; +} + +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + + conn->Flags &= ~GETTINGTITLE; + + if (msglen == 1) + { + nodeprintf(conn, "*** Message Cancelled\r"); + SendPrompt(conn, user); + return; + } + + if (msglen > 60) msglen = 60; + + Buffer[msglen-1] = 0; + + strcpy(conn->TempMsg->title, Buffer); + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to create Message Buffer\r"); + return; + } + + conn->Flags |= GETTINGMESSAGE; + + if (!conn->BBSFlags & BBS) + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + +} + +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + char * ptr2 = NULL; + + if (((msglen < 3) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3) == 0))) + { + int Index = 0; + + if (conn->TempMsg->type == 'P') + Index = PMSG; + else if (conn->TempMsg->type == 'B') + Index = BMSG; + else if (conn->TempMsg->type == 'T') + Index = TMSG; + + conn->Flags &= ~GETTINGMESSAGE; + + user->Total.MsgsReceived[Index]++; + user->Total.BytesForwardedIn[Index] += conn->TempMsg->length; + + if (conn->ToCount) + { + // Multiple recipients + + struct MsgInfo * Msg = conn->TempMsg; + int i; + struct MsgInfo * SaveMsg = Msg; + char * SaveBody = conn->MailBuffer; + int SaveMsgLen = Msg->length; + BOOL SentToRMS = FALSE; + int ToLen = 0; + char * ToString = zalloc(conn->ToCount * 100); + + // If no BID provided, allocate one + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg + 1, BBSName); + + for (i = 0; i < conn->ToCount; i++) + { + char * Addr = conn->To[i]; + char * Via; + + if (_memicmp (Addr, "SMTP:", 5) == 0) + { + // For Email + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + Msg->to[0] = 0; + strcpy(Msg->via, &Addr[5]); + + CreateMessageFromBuffer(conn); + continue; + } + + if (_memicmp (Addr, "RMS:", 4) == 0) + { + // Add to B2 Message for RMS + + Addr+=4; + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + goto PktMsg; + } + else + { + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + continue; + } + } + + ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, Addr, Via); + continue; + } + + _strupr(Addr); + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + } + else + { + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + + // Add to B2 Message for RMS + + continue; + } + } + + PktMsg: + + conn->LocalMsg = FALSE; + + // Normal BBS Message + + if (_stricmp(Addr, "SYSOP") == 0) + conn->LocalMsg = TRUE; + else + { + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + conn->LocalMsg = TRUE; + } + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + strcpy(Msg->to, Addr); + + if (Via) + { + Msg->bid[0] = 0; // if we are forwarding it, we must change BID to be safe + strcpy(Msg->via, Via); + } + + CreateMessageFromBuffer(conn); + } + + if (ToLen) + { + char * B2Hddr = zalloc(ToLen + 1000); + int B2HddrLen; + char DateString[80]; + struct tm * tm; + time_t Date = time(NULL); + char Type[16] = "Private"; + + // Get Type + + if (conn->TempMsg->type == 'B') + strcpy(Type, "Bulletin"); + else if (conn->TempMsg->type == 'T') + strcpy(Type, "Traffic"); + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000 + ToLen); + + Msg->B2Flags = B2Msg; + + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n", + SaveMsg->bid, DateString, Type, + SaveMsg->from, ToString, SaveMsg->title, BBSName, SaveMsgLen); + + memcpy(conn->MailBuffer, B2Hddr, B2HddrLen); + memcpy(&conn->MailBuffer[B2HddrLen], SaveBody, SaveMsgLen); + + Msg->length += B2HddrLen; + + strcpy(Msg->to, "RMS"); + + CreateMessageFromBuffer(conn); + + free(B2Hddr); + } + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + if (!(conn->BBSFlags & BBS)) + SendPrompt(conn, conn->UserPointer); + else + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->BBSFlags & OUTWARDCONNECT) + BBSputs(conn, "F>\r"); // if Outward connect must be reverse forward + else + BBSputs(conn, ">\r"); + } + + /* + // From a client - Create one copy with all RMS recipients, and another for each packet recipient + + // Merge all RMS To: lines + + ToLen = 0; + ToString[0] = 0; + + for (i = 0; i < Recipients; i++) + { + if (LocalMsg[i]) + continue; // For a local RMS user + + if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + { + ToLen += strlen(HddrTo[i]); + strcat(ToString, HddrTo[i]); + } + } + + if (ToLen) + { + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], ToString, ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, "RMS"); + strcpy(Msg->via, "winlink.org"); + + // Must Change the BID + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + + } + + free(ToString); + + for (i = 0; i < Recipients; i++) + { + // Only Process Non - RMS Dests or local RMS Users + + if (LocalMsg[i] == 0) + if (_stricmp (Via[i], "WINLINK.ORG") == 0 || + _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ToLen = strlen(HddrTo[i]); + + if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + } // End not from RMS + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + SetupNextFBBMessage(conn); + return; + + } My__except_Routine("Process Multiple Destinations"); + + BBSputs(conn, "*** Program Error Processing Multiple Destinations\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; +*/ + + conn->ToCount = 0; + + return; + } + + + CreateMessageFromBuffer(conn); + return; + + } + + Buffer[msglen++] = 0x0a; + + if ((conn->TempMsg->length + msglen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, msglen); + + conn->TempMsg->length += msglen; +} + +VOID CreateMessageFromBuffer(CIRCUIT * conn) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * ptr1, * ptr2 = NULL; + char * ptr3, * ptr4; + int FWDCount = 0; + char OldMess[] = "\r\n\r\nOriginal Message:\r\n\r\n"; + time_t Age; + int OurCount; + char * HoldReason = "User has Hold Messages flag set"; + struct UserInfo * user; + + +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; +#endif + + // If doing SC, Append Old Message + + if (conn->CopyBuffer) + { + if ((conn->TempMsg->length + (int) strlen(conn->CopyBuffer) + 80 )> conn->MailBufferSize) + { + conn->MailBufferSize += (int)strlen(conn->CopyBuffer) + 80; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], OldMess, strlen(OldMess)); + + conn->TempMsg->length += (int)strlen(OldMess); + + memcpy(&conn->MailBuffer[conn->TempMsg->length], conn->CopyBuffer, strlen(conn->CopyBuffer)); + + conn->TempMsg->length += (int)strlen(conn->CopyBuffer); + + free(conn->CopyBuffer); + conn->CopyBuffer = NULL; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + memcpy(Msg, conn->TempMsg, sizeof(struct MsgInfo)); + + free(conn->TempMsg); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + if (Msg->status == 0) + Msg->status = 'N'; + + // Create BID if non supplied + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + // if message body had R: lines, get date created from last (not very accurate, but best we can do) + + // Also check if we have had message before to detect loops + + ptr1 = conn->MailBuffer; + OurCount = 0; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr1 = strstr(ptr1, "\r\n\r\n"); + if (ptr1) + ptr1 += 4; + else + ptr1 = conn->MailBuffer; + } + +nextline: + + if (memcmp(ptr1, "R:", 2) == 0) + { + // Is if ours? + + // BPQ RLINE Format R:090920/1041Z 6542@N4JOA.#WPBFL.FL.USA.NOAM BPQ1.0.2 + + ptr3 = strchr(ptr1, '@'); + ptr4 = strchr(ptr1, '.'); + + if (ptr3 && ptr4 && (ptr4 > ptr3)) + { + if (memcmp(ptr3+1, BBSName, ptr4-ptr3-1) == 0) + OurCount++; + } + + GetWPBBSInfo(ptr1); // Create WP /I record from R: Line + + // see if another + + ptr2 = ptr1; // save + ptr1 = strchr(ptr1, '\r'); + if (ptr1 == 0) + { + Debugprintf("Corrupt Message %s from %s - truncated within R: line", Msg->bid, Msg->from); + return; + } + ptr1++; + if (*ptr1 == '\n') ptr1++; + + goto nextline; + } + + // ptr2 points to last R: line (if any) + + if (ptr2) + { + struct tm rtime; + time_t result; + + memset(&rtime, 0, sizeof(struct tm)); + + if (ptr2[10] == '/') + { + // Dodgy 4 char year + + sscanf(&ptr2[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (ptr2[8] == '/') + { + sscanf(&ptr2[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + // result = _mkgmtime(&rtime); + + if ((result = mktime(&rtime)) != (time_t)-1 ) + { + result -= (time_t)_MYTIMEZONE; + + Msg->datecreated = result; + Age = (time(NULL) - result)/86400; + + if ( Age < -7) + { + Msg->status = 'H'; + HoldReason = "Suspect Date Sent"; + } + else if (Age > BidLifetime || Age > MaxAge) + { + Msg->status = 'H'; + HoldReason = "Message too old"; + + } + else + GetWPInfoFromRLine(Msg->from, ptr2, result); + } + else + { + // Can't decode R: Datestamp + + Msg->status = 'H'; + HoldReason = "Corrupt R: Line - can't determine age"; + } + + if (OurCount > 1) + { + // Message is looping + + Msg->status = 'H'; + HoldReason = "Message may be looping"; + + } + } + + if (strcmp(Msg->to, "WP") == 0) + { + // If Reject WP Bulls is set, Kill message here. + // It should only get here if B2 - otherwise it should be + // rejected earlier + + if (Msg->type == 'B' && FilterWPBulls) + Msg->status = 'K'; + + } + + conn->MailBuffer[Msg->length] = 0; + + if (CheckBadWords(Msg->title) || CheckBadWords(conn->MailBuffer)) + { + Msg->status = 'H'; + HoldReason = "Bad word in title or body"; + } + + if (CheckHoldFilters(Msg->from, Msg->to, Msg->via, Msg->bid)) + { + Msg->status = 'H'; + HoldReason = "Matched Hold Filters"; + } + + if (CheckValidCall(Msg->from) == 0) + { + Msg->status = 'H'; + HoldReason = "Probable Invalid From Call"; + } + + // Process any WP Messages + + if (strcmp(Msg->to, "WP") == 0) + { + if (Msg->status == 'N') + { + ProcessWPMsg(conn->MailBuffer, Msg->length, ptr2); + + if (Msg->type == 'P') // Kill any processed private WP messages. + { + char VIA[80]; + + strcpy(VIA, Msg->via); + strlop(VIA, '.'); + + if (strcmp(VIA, BBSName) == 0) + Msg->status = 'K'; + } + } + } + + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (Msg->length > MaxTXSize) + { + Msg->status = 'H'; + HoldReason = "Message too long"; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "*** Warning Message length exceeds sysop-defined maximum of %d - Message will be held\r", MaxTXSize); + } + + // Check for message to internal server + + if (Msg->via[0] == 0 + || _stricmp(Msg->via, BBSName) == 0 // our BBS a + || _stricmp(Msg->via, AMPRDomain) == 0) // our AMPR Address + { + if (CheckforMessagetoServer(Msg)) + { + // Flag as killed and send prompt + + FlagAsKilled(Msg, TRUE); + + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message %d to Server Processed and Killed.\r", Msg->number); + SendPrompt(conn, conn->UserPointer); + } + return; // no need to process further + } + } + + if (Msg->to[0]) + FWDCount = MatchMessagetoBBSList(Msg, conn); + else + { + // If addressed @winlink.org, and to a local user, Keep here. + + char * Call; + char * AT; + + // smtp or rms - don't warn no route + + FWDCount = 1; + + Call = _strupr(_strdup(Msg->via)); + AT = strlop(Call, '@'); + + if (AT && _stricmp(AT, "WINLINK.ORG") == 0) + { + struct UserInfo * user = LookupCall(Call); + + if (user) + { + if (user->flags & F_POLLRMS) + { + Logprintf(LOG_BBS, conn, '?', "SMTP Message @ winlink.org, but local RMS user - leave here"); + strcpy(Msg->to, Call); + strcpy(Msg->via, AT); + if (user->flags & F_BBS) // User is a BBS, so set FWD bit so he can get it + set_fwd_bit(Msg->fbbs, user->BBSNumber); + + } + } + } + free(Call); + } + + // Warn SYSOP if P or T forwarded in, and has nowhere to go + + if ((conn->BBSFlags & BBS) && Msg->type != 'B' && FWDCount == 0 && WarnNoRoute && + strcmp(Msg->to, "SYSOP") && strcmp(Msg->to, "WP")) + { + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + SendWarningToSYSOP(Msg); + } + else + { + // No via - is it for a local user? + + if (LookupCall(Msg->to) == 0) + SendWarningToSYSOP(Msg); + } + } + + if ((conn->BBSFlags & SYNCMODE) == 0) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message: %d Bid: %s Size: %d\r", Msg->number, Msg->bid, Msg->length); + + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + + if (FWDCount == 0 && Msg->to[0] != 0) // unless smtp msg + nodeprintf(conn, "@BBS specified, but no forwarding info is available - msg may not be delivered\r"); + } + else + { + if (FWDCount == 0 && conn->LocalMsg == 0 && Msg->type != 'B') + // Not Local and no forward route + nodeprintf(conn, "Message is not for a local user, and no forwarding info is available - msg may not be delivered\r"); + } + if (conn->ToCount == 0) + SendPrompt(conn, conn->UserPointer); + } + else + { + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->ToCount == 0) + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + } + } + + if(Msg->to[0] == 0) + SMTPMsgCreated=TRUE; + + if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + if (Msg->status == 'H') + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, HoldReason); + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + // If Event Notifications enabled report a new message event + + if (reportNewMesageEvents) + { + char msg[200]; + + //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + struct tm *tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(msg, sizeof(msg),"%-6d %c %6d %-13s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->length, Msg->to, + Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); + +#ifdef WIN32 + if (pRunEventProgram) + pRunEventProgram("MailNewMsg.exe", msg); +#else + { + char prog[256]; + sprintf(prog, "%s/%s", BPQDirectory, "MailNewMsg"); + RunEventProgram(prog, msg); + } +#endif + } + + + if (EnableUI) +#ifdef LINBPQ + SendMsgUI(Msg); +#else + __try + { + SendMsgUI(Msg); + } + My__except_Routine("SendMsgUI"); +#endif + + user = LookupCall(Msg->to); + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + return; +} + +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg) +{ + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + char Mess[255]; + int len; + BOOL AutoImport = FALSE; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + // If title is "Batched messages for AutoImport from BBS xxxxxx and first line is S? and it is + // for this BBS, Import file and set message as Killed. May need to strip B2 Header and R: lines + + if (strcmp(Msg->to, BBSName) == 0 && strstr(Msg->title, "Batched messages for AutoImport from BBS ")) + { + UCHAR * ptr = conn->MailBuffer; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr = strstr(ptr, "\r\n\r\n"); + if (ptr) + ptr += 4; + else + ptr = conn->MailBuffer; + } + + if (memcmp(ptr, "R:", 2) == 0) + { + ptr = strstr(ptr, "\r\n\r\n"); //And remove R: Lines + if (ptr) + ptr += 4; + } + + if (*(ptr) == 'S' && ptr[2] == ' ') + { + int HeaderLen = (int)(ptr - conn->MailBuffer); + Msg->length -= HeaderLen; + memmove(conn->MailBuffer, ptr, Msg->length); + Msg->status = 'K'; + AutoImport = TRUE; + } + } + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(conn->MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + if (AutoImport) + ImportMessages(NULL, MsgFile, TRUE); + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (WriteLen != Msg->length) + { + len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); + QueueMsg(conn, Mess, len); + Debugprintf(Mess); + } + return; +} + + + + +VOID SendUnbuffered(int stream, char * msg, int len) +{ +#ifndef LINBPQ + if (stream < 0) + WritetoConsoleWindow(stream, msg, len); + else +#endif + SendMsg(stream, msg, len); +} + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen); + +BOOL FindMessagestoForward (CIRCUIT * conn) +{ + struct UserInfo * user = conn->UserPointer; + +#ifndef LINBPQ + + struct _EXCEPTION_POINTERS exinfo; + + __try { +#endif + + // This is a hack so Winpack or WLE users can use forwarding + // protocols to get their messages without being defined as a BBS + + // !!IMPORTANT Getting this wrong can see message repeatedly proposed !! + // !! Anything sent using this must be killed if sent or rejected. + + // I'm not sure about this. I think I only need the PacLinkCalls + // if from RMS Express, and it always sends an FW; line + // Ah, no. What about WinPack ?? + // If from RMS Express must have Temp_B2 or BBS set or SID will + // be rejected. So maybe just Temp_B2 && BBS = 0?? + // No, someone may have Temp_B2 set and not be using WLE ?? So what ?? + + if ((user->flags & F_Temp_B2_BBS) && ((user->flags & F_BBS) == 0) || conn->RMSExpress || conn->PAT) + { + if (conn->PacLinkCalls == NULL) + { + // create a list with just the user call + + char * ptr1; + + conn->PacLinkCalls = zalloc(30); + + ptr1 = (char *)conn->PacLinkCalls; + ptr1 += 16; // Must be room for a null pointer on end (64 bit bug) + strcpy(ptr1, user->Call); + + conn->PacLinkCalls[0] = ptr1; + } + } + + if (conn->SendT && FindMessagestoForwardLoop(conn, 'T', conn->MaxTLen)) + { + conn->LastForwardType = 'T'; + return TRUE; + } + + if (conn->LastForwardType == 'T') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendP && FindMessagestoForwardLoop(conn, 'P', conn->MaxPLen)) + { + conn->LastForwardType = 'P'; + return TRUE; + } + + if (conn->LastForwardType == 'P') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendB && FindMessagestoForwardLoop(conn, 'B', conn->MaxBLen)) + { + conn->LastForwardType = 'B'; + return TRUE; + } + + conn->LastForwardType = 0; + return FALSE; +#ifndef LINBPQ + } My__except_Routine("FindMessagestoForward"); +#endif + return FALSE; + +} + + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen) +{ + // See if any messages are queued for this BBS + + int m; + struct MsgInfo * Msg; + struct UserInfo * user = conn->UserPointer; + struct FBBHeaderLine * FBBHeader; + BOOL Found = FALSE; + char RLine[100]; + int TotalSize = 0; + time_t NOW = time(NULL); + +// Debugprintf("FMTF entered Call %s Type %c Maxlen %d NextMsg = %d BBSNo = %d", +// conn->Callsign, Type, MaxLen, conn->NextMessagetoForward, user->BBSNumber); + + if (conn->PacLinkCalls || (conn->UserPointer->flags & F_NTSMPS)) // Looking for all messages, so reset + conn->NextMessagetoForward = 1; + + conn->FBBIndex = 0; + + for (m = conn->NextMessagetoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If an NTS MPS, see if anything matches + + if (Type == 'T' && (conn->UserPointer->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + int depth; + + if (Msg->type == 'T' && Msg->status == 'N' && Msg->length <= MaxLen && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + } + } + + // If forwarding to Paclink or RMS Express, look for any message matching the + // requested call list with status 'N' (maybe should also be 'P' ??) + + if (conn->PacLinkCalls) + { + int index = 1; + + char * Call = conn->PacLinkCalls[0]; + + while (Call) + { + if (Msg->type == Type && Msg->status == 'N') + { +// Debugprintf("Comparing RMS Call %s %s", Msg->to, Call); + if (_stricmp(Msg->to, Call) == 0) + if (Msg->status == 'N' && Msg->type == Type && Msg->length <= MaxLen) + goto Forwardit; + else + Debugprintf("Call Match but Wrong Type/Len %c %d", Msg->type, Msg->length); + } + Call = conn->PacLinkCalls[index++]; + } +// continue; + } + + if (Msg->type == Type && Msg->length <= MaxLen && (Msg->status != 'H') + && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + // Message to be sent - do a consistancy check (State, etc) + + Forwardit: + + if (Msg->Defered > 0) // = response received + { + Msg->Defered--; + Debugprintf("Message %d deferred", Msg->number); + continue; + } + + if ((Msg->from[0] == 0) || (Msg->to[0] == 0)) + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Missing From: or To: field"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + Msg->status = 'H'; + continue; + } + + conn->NextMessagetoForward = m + 1; // So we don't offer again if defered + + Msg->Locked = 1; // So other MPS can't pick it up + + // if FBB forwarding add to list, eise save pointer + + if (conn->BBSFlags & FBBForwarding) + { + struct tm *tm; + time_t temp; + + FBBHeader = &conn->FBBHeaders[conn->FBBIndex++]; + + FBBHeader->FwdMsg = Msg; + FBBHeader->MsgType = Msg->type; + FBBHeader->Size = Msg->length; + TotalSize += Msg->length; + strcpy(FBBHeader->From, Msg->from); + strcpy(FBBHeader->To, Msg->to); + strcpy(FBBHeader->ATBBS, Msg->via); + strcpy(FBBHeader->BID, Msg->bid); + + // Set up R:Line, so se can add its length to the sise + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + FBBHeader->Size += sprintf_s(RLine, sizeof(RLine),"R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + // If using B2 forwarding we need the message size and Compressed size for FC proposal + + if (conn->BBSFlags & FBBB2Mode) + { + if (CreateB2Message(conn, FBBHeader, RLine) == FALSE) + { + char * MailBuffer = malloc(100); + char Title[100]; + int Length; + + // Corrupt B2 Message + + Debugprintf("Corrupt B2 Message found - Message %d will be held", Msg->number); + Msg->status = 'H'; + SaveMessageDatabase(); + + conn->FBBIndex--; + TotalSize -= Msg->length; + memset(&conn->FBBHeaders[conn->FBBIndex], 0, sizeof(struct FBBHeaderLine)); + + Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Corrupt B2 Message"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + continue; + } + } + + if (conn->FBBIndex == 5 || TotalSize > user->ForwardingInfo->MaxFBBBlockSize) + return TRUE; // Got max number or too big + + Found = TRUE; // Remember we have some + } + else + { + conn->FwdMsg = Msg; + return TRUE; + } + } + } + + return Found; +} + +BOOL SeeifMessagestoForward (int BBSNumber, CIRCUIT * conn) +{ + // See if any messages are queued for this BBS + + // if Conn is not NULL, also check Msg Type + + int m; + struct MsgInfo * Msg; + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + if (conn) + { + char Type = Msg->type; + + if ((conn->SendB && Type == 'B') || (conn->SendP && Type == 'P') || (conn->SendT && Type == 'T')) + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + else + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + } + + return FALSE; +} + +int CountMessagestoForward (struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + n++; + continue; // So we dont count twice in Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + n++; + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + } + } + } + + return n; +} + +int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; // So we dont count twice in Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + } + } + } + + return n; +} + +VOID SendWarningToSYSOP(struct MsgInfo * Msg) +{ + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Warning - Message %d has nowhere to go", Msg->number); + sprintf(Title, "Warning - Message %d has nowhere to go", Msg->number); + SendMessageToSYSOP(Title, MailBuffer, Length); +} + + + +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, "SYSTEM"); + if (SendSYStoSYSOPCall) + strcpy(Msg->to, SYSOPCall); + else + strcpy(Msg->to, "SYSOP"); + + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +VOID CheckBBSNumber(int i) +{ + // Make sure number is unique + + int Count = 0; + struct UserInfo * user; + + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + { + Count++; + + if (Count > 1) + { + // Second with same number - Renumber this one + + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + + Logprintf(LOG_BBS, NULL, '?', "Duplicate BBS Number found. BBS %s Old BBSNumber %d New BBS Number %d", user->Call, i, user->BBSNumber); + + } + } + } +} + + +int FindFreeBBSNumber() +{ + // returns the lowest number not used by any bbs or message. + + struct MsgInfo * Msg; + struct UserInfo * user; + int i, m; + + for (i = 1; i<= NBBBS; i++) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + goto nexti; // In use + } + + // Not used by BBS - check messages + + for (m = 1; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if (check_fwd_bit(Msg->fbbs, i)) + goto nexti; // In use + + if (check_fwd_bit(Msg->forw, i)) + goto nexti; // In use + } + + // Not in Use + + return i; + +nexti:; + + } + + return 0; // All used +} + +BOOL SetupNewBBS(struct UserInfo * user) +{ + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + return FALSE; + + user->BBSNext = BBSChain; + BBSChain = user; + + SortBBSChain(); + + ReinitializeFWDStruct(user); + + return TRUE; +} + +VOID DeleteBBS(struct UserInfo * user) +{ + struct UserInfo * BBSRec, * PrevBBS = NULL; + +#ifndef LINBPQ + RemoveMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, MF_BYCOMMAND); +#endif + for (BBSRec = BBSChain; BBSRec; PrevBBS = BBSRec, BBSRec = BBSRec->BBSNext) + { + if (user == BBSRec) + { + if (PrevBBS == NULL) // First in chain; + { + BBSChain = BBSRec->BBSNext; + break; + } + PrevBBS->BBSNext = BBSRec->BBSNext; + break; + } + } +} + + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo); + +VOID SetupForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + + char Key[100] = "BBSForwarding."; + char Temp[100]; + + HKEY hKey=0; + char RegKey[100] = "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\BBSForwarding\\"; + + int m; + struct MsgInfo * Msg; + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + if (UsingingRegConfig == 0) + { + // Config from file + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + strcat(Key, "*"); + + strcat(Key, user->Call); + + group = config_lookup (&cfg, Key); + + if (group == NULL) // No info + return; + else + { + ForwardingInfo->TOCalls = GetMultiStringValue(group, "TOCalls"); + ForwardingInfo->ConnectScript = GetMultiStringValue(group, "ConnectScript"); + ForwardingInfo->ATCalls = GetMultiStringValue(group, "ATCalls"); + ForwardingInfo->Haddresses = GetMultiStringValue(group, "HRoutes"); + ForwardingInfo->HaddressesP = GetMultiStringValue(group, "HRoutesP"); + ForwardingInfo->FWDTimes = GetMultiStringValue(group, "FWDTimes"); + + ForwardingInfo->Enabled = GetIntValue(group, "Enabled"); + ForwardingInfo->ReverseFlag = GetIntValue(group, "RequestReverse"); + ForwardingInfo->AllowBlocked = GetIntValue(group, "AllowBlocked"); + ForwardingInfo->AllowCompressed = GetIntValue(group, "AllowCompressed"); + ForwardingInfo->AllowB1 = GetIntValue(group, "UseB1Protocol"); + ForwardingInfo->AllowB2 = GetIntValue(group, "UseB2Protocol"); + ForwardingInfo->SendCTRLZ = GetIntValue(group, "SendCTRLZ"); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + if (ForwardingInfo->AllowCompressed) + ForwardingInfo->AllowBlocked = TRUE; + + ForwardingInfo->PersonalOnly = GetIntValue(group, "FWDPersonalsOnly"); + ForwardingInfo->SendNew = GetIntValue(group, "FWDNewImmediately"); + ForwardingInfo->FwdInterval = GetIntValue(group, "FwdInterval"); + ForwardingInfo->RevFwdInterval = GetIntValue(group, "RevFWDInterval"); + ForwardingInfo->MaxFBBBlockSize = GetIntValue(group, "MaxFBBBlock"); + ForwardingInfo->ConTimeout = GetIntValue(group, "ConTimeout"); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + if (ForwardingInfo->ConTimeout == 0) + ForwardingInfo->ConTimeout = 120; + + GetStringValue(group, "BBSHA", Temp); + + if (Temp[0]) + ForwardingInfo->BBSHA = _strdup(Temp); + else + ForwardingInfo->BBSHA = _strdup(""); + } + } + else + { +#ifndef LINBPQ + + int retCode,Type,Vallen; + + strcat(RegKey, user->Call); + retCode = RegOpenKeyEx (REGTREE, RegKey, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode != ERROR_SUCCESS) + return; + else + { + ForwardingInfo->ConnectScript = RegGetMultiStringValue(hKey, "Connect Script"); + ForwardingInfo->TOCalls = RegGetMultiStringValue(hKey, "TOCalls"); + ForwardingInfo->ATCalls = RegGetMultiStringValue(hKey, "ATCalls"); + ForwardingInfo->Haddresses = RegGetMultiStringValue(hKey, "HRoutes"); + ForwardingInfo->HaddressesP = RegGetMultiStringValue(hKey, "HRoutesP"); + ForwardingInfo->FWDTimes = RegGetMultiStringValue(hKey, "FWD Times"); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Enabled", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->Enabled,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "RequestReverse", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->ReverseFlag,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "AllowCompressed", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowCompressed,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B1 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB1,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B2 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB2,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "SendCTRLZ", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendCTRLZ,(ULONG *)&Vallen); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD Personals Only", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->PersonalOnly,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD New Immediately", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendNew,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"FWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->FwdInterval,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"RevFWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->RevFwdInterval,(ULONG *)&Vallen); + + RegQueryValueEx(hKey,"MaxFBBBlock",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->MaxFBBBlockSize,(ULONG *)&Vallen); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + Vallen=0; + retCode = RegQueryValueEx(hKey,"BBSHA",0 , (ULONG *)&Type,NULL, (ULONG *)&Vallen); + + if (retCode != 0) + { + // No Key - Get from WP?? + + WPRec * ptr = LookupWP(user->Call); + + if (ptr) + { + if (ptr->first_homebbs) + { + ForwardingInfo->BBSHA = _strdup(ptr->first_homebbs); + } + } + } + + if (Vallen) + { + ForwardingInfo->BBSHA = malloc(Vallen); + RegQueryValueEx(hKey, "BBSHA", 0, (ULONG *)&Type, ForwardingInfo->BBSHA,(ULONG *)&Vallen); + } + + RegCloseKey(hKey); + } +#endif + } + + // Convert FWD Times and H Addresses + + if (ForwardingInfo->FWDTimes) + SetupFwdTimes(ForwardingInfo); + + if (ForwardingInfo->Haddresses) + SetupHAddreses(ForwardingInfo); + + if (ForwardingInfo->HaddressesP) + SetupHAddresesP(ForwardingInfo); + + if (ForwardingInfo->BBSHA) + { + if (ForwardingInfo->BBSHA[0]) + SetupHAElements(ForwardingInfo); + else + { + free(ForwardingInfo->BBSHA); + ForwardingInfo->BBSHA = NULL; + } + } + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If any forward bits are set, increment count on BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount++; + } + } + } +} + +VOID * GetMultiStringValue(config_setting_t * group, char * ValueName) +{ + char * ptr1; + char * MultiString = NULL; + const char * ptr; + int Count = 0; + char ** Value; + config_setting_t *setting; + char * Save; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + setting = config_setting_get_member (group, ValueName); + + if (setting) + { + ptr = config_setting_get_string (setting); + + Save = _strdup(ptr); // DOnt want to change config string + ptr = Save; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) // ignore null elements + { + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + free(Save); + } + + Value[Count] = NULL; + return Value; +} + + +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName) +{ +#ifdef LINBPQ + return NULL; +#else + int retCode,Type,Vallen; + char * MultiString = NULL; + int ptr, len; + int Count = 0; + char ** Value; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + + Value[0] = NULL; + + Vallen=0; + + + retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if ((retCode != 0) || (Vallen < 3)) // Two nulls means empty multistring + { + free(Value); + return FALSE; + } + + MultiString = malloc(Vallen); + + retCode = RegQueryValueEx(hKey, ValueName, 0, + (ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen); + + ptr=0; + + while (MultiString[ptr]) + { + len=strlen(&MultiString[ptr]); + + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(&MultiString[ptr]); + ptr+= (len + 1); + } + + Value[Count] = NULL; + + free(MultiString); + + return Value; +#endif +} + +VOID FreeForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + int i; + + + ForwardingInfo = user->ForwardingInfo; + + FreeList(ForwardingInfo->TOCalls); + FreeList(ForwardingInfo->ATCalls); + FreeList(ForwardingInfo->Haddresses); + FreeList(ForwardingInfo->HaddressesP); + + i=0; + if(ForwardingInfo->HADDRS) + { + while(ForwardingInfo->HADDRS[i]) + { + FreeList(ForwardingInfo->HADDRS[i]); + i++; + } + free(ForwardingInfo->HADDRS); + free(ForwardingInfo->HADDROffet); + } + + i=0; + if(ForwardingInfo->HADDRSP) + { + while(ForwardingInfo->HADDRSP[i]) + { + FreeList(ForwardingInfo->HADDRSP[i]); + i++; + } + free(ForwardingInfo->HADDRSP); + } + + FreeList(ForwardingInfo->ConnectScript); + FreeList(ForwardingInfo->FWDTimes); + if (ForwardingInfo->FWDBands) + { + i=0; + while(ForwardingInfo->FWDBands[i]) + { + free(ForwardingInfo->FWDBands[i]); + i++; + } + free(ForwardingInfo->FWDBands); + } + if (ForwardingInfo->BBSHAElements) + { + i=0; + while(ForwardingInfo->BBSHAElements[i]) + { + free(ForwardingInfo->BBSHAElements[i]); + i++; + } + free(ForwardingInfo->BBSHAElements); + } + free(ForwardingInfo->BBSHA); + +} + +VOID FreeList(char ** Hddr) +{ + VOID ** Save; + + if (Hddr) + { + Save = (void **)Hddr; + while(Hddr[0]) + { + free(Hddr[0]); + Hddr++; + } + free(Save); + } +} + + +VOID ReinitializeFWDStruct(struct UserInfo * user) +{ + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + SetupForwardingStruct(user); + +} + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Times = ForwardingInfo->FWDTimes; + int Start, End; + int Count = 0; + + ForwardingInfo->FWDBands = zalloc(sizeof(struct FWDBAND)); + + if (Times) + { + while(Times[0]) + { + ForwardingInfo->FWDBands = realloc(ForwardingInfo->FWDBands, (Count+2)* sizeof(struct FWDBAND)); + ForwardingInfo->FWDBands[Count] = zalloc(sizeof(struct FWDBAND)); + + Start = atoi(Times[0]); + End = atoi(&Times[0][5]); + + ForwardingInfo->FWDBands[Count]->FWDStartBand = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + ForwardingInfo->FWDBands[Count]->FWDEndBand = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + Count++; + Times++; + } + ForwardingInfo->FWDBands[Count] = NULL; + } +} +void StartForwarding(int BBSNumber, char ** TempScript) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + + if ((BBSNumber == 0) || (user->BBSNumber == BBSNumber)) + if (ForwardingInfo) + if (ForwardingInfo->Enabled || BBSNumber) // Menu Command overrides enable + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + if (BBSNumber || SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) // Menu Command overrides Reverse + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + // See if TempScript requested + + if (user->ForwardingInfo->TempConnectScript) + FreeList(user->ForwardingInfo->TempConnectScript); + + user->ForwardingInfo->TempConnectScript = TempScript; + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + + return; +} + +size_t fwritex(CIRCUIT * conn, void * _Str, size_t _Size, size_t _Count, FILE * _File) +{ + if (_File) + return fwrite(_Str, _Size, _Count, _File); + + // Appending to MailBuffer + + memcpy(&conn->MailBuffer[conn->InputLen], _Str, _Count); + conn->InputLen += (int)_Count; + + return _Count; +} + + +BOOL ForwardMessagestoFile(CIRCUIT * conn, char * FN) +{ + BOOL AddCRLF = FALSE; + BOOL AutoImport = FALSE; + FILE * Handle = NULL; + char * Context; + BOOL Email = FALSE; + time_t now = time(NULL); + char * param; + + FN = strtok_s(FN, " ,", &Context); + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + } + } + // If FN is an email address, write to a temp file, and send via rms or emali gateway + + if (strchr(FN, '@') || _memicmp(FN, "RMS:", 4) == 0) + { + Email = TRUE; + AddCRLF =TRUE; + conn->MailBuffer=malloc(100000); + conn->MailBufferSize=100000; + conn->InputLen = 0; + } + else + { + Handle = fopen(FN, "ab"); + + if (Handle == NULL) + { + int err = GetLastError(); + Logprintf(LOG_BBS, conn, '!', "Failed to open Export File %s", FN); + return FALSE; + } + } + + while (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + struct tm * tm; + time_t temp; + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + char * MsgPtr; + char Line[256]; + int len; + struct UserInfo * user = conn->UserPointer; + int Index = 0; + + Msg = conn->FwdMsg; + + if (Email) + if (conn->InputLen + Msg->length + 500 > conn->MailBufferSize) + break; + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwritex(conn, Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwritex(conn, Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr = strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + fwritex(conn, Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwritex(conn, "\r\n", 1, 2, Handle); + + fwritex(conn, MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwritex(conn, "/EX\r\n", 1, 5, Handle); + else + fwritex(conn, "\r\n/EX\r\n", 1, 7, Handle); + + if (AddCRLF) + fwritex(conn, "\r\n", 1, 2, Handle); + + free(MsgBytes); + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + Msg->datechanged = now; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->UserPointer->ForwardingInfo->MsgCount--; + } + + if (Email) + { + struct MsgInfo * Msg; + BIDRec * BIDRec; + + if (conn->InputLen == 0) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + return TRUE; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datecreated = Msg->datechanged = Msg->datereceived = now; + + strcpy(Msg->from, BBSName); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + if (AutoImport) + sprintf(Msg->title, "Batched messages for AutoImport from BBS %s", BBSName); + else + sprintf(Msg->title, "Batched messages from BBS %s", BBSName); + + Msg->length = conn->InputLen; + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (_memicmp(FN, "SMTP:", 5) == 0) + { + strcpy(Msg->via, &FN[5]); + SMTPMsgCreated=TRUE; + } + else + { + strcpy(Msg->to, "RMS"); + if (_memicmp(FN, "RMS:", 4) == 0) + strcpy(Msg->via, &FN[4]); + else + strcpy(Msg->via, FN); + } + + MatchMessagetoBBSList(Msg, conn); + + SaveMessageDatabase(); + SaveBIDDatabase(); + } + else + fclose(Handle); + + SaveMessageDatabase(); + return TRUE; +} + +BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle) +{ + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(Msg->number); + char * MsgPtr; + char Line[256]; + int len; + int MsgLen = Msg->length; + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwrite(Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwrite(Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + MsgLen = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + + // If a B2 Message, remove B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + fwrite(Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwrite("\r\n", 1, 2, Handle); + + fwrite(MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwrite("/EX\r\n", 1, 5, Handle); + else + fwrite("\r\n/EX\r\n", 1, 7, Handle); + + free(MsgBytes); + + return TRUE; + +} + +BOOL ConnecttoBBS (struct UserInfo * user) +{ + int n, p; + CIRCUIT * conn; + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + + for (n = NumberofStreams-1; n >= 0 ; n--) + { + conn = &Connections[n]; + + if (conn->Active == FALSE) + { + p = conn->BPQStream; + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->BPQStream = p; + + // Can't set Active until Connected or Stuck Session detertor can clear session. + // But must set Active before Connected() runs or will appear is Incoming Connect. + // Connected() is semaphored, so get semaphore before ConnectUsingAppl + // Probably better to semaphore lost session code instead + + + strcpy(conn->Callsign, user->Call); + conn->BBSFlags |= (RunningConnectScript | OUTWARDCONNECT); + conn->UserPointer = user; + + Logprintf(LOG_BBS, conn, '|', "Connecting to BBS %s", user->Call); + + ForwardingInfo->MoreLines = TRUE; + + GetSemaphore(&ConSemaphore, 1); + conn->Active = TRUE; + ConnectUsingAppl(conn->BPQStream, BBSApplMask); + FreeSemaphore(&ConSemaphore); + + // If we are sending to a dump pms we may need to connect using the message sender's callsign. + // But we wont know until we run the connect script, which is a bit late to change call. Could add + // flag to forwarding config, but easier to look for SETCALLTOSENDER in the connect script. + + if (strstr(ForwardingInfo->ConnectScript[0], "SETCALLTOSENDER")) + { + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + // We have a message to send + + struct MsgInfo * Msg; + unsigned char AXCall[7]; + + Msg = conn->FwdMsg; + ConvToAX25(Msg->from, AXCall); + ChangeSessionCallsign(p, AXCall); + + conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER | NEWPACCOM; + conn->NextMessagetoForward = 0; // was set by FindMessages + } + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + } +#ifdef LINBPQ + { + BPQVECSTRUC * SESS; + SESS = &BPQHOSTVECTOR[conn->BPQStream - 1]; + + if (SESS->HOSTSESSION == NULL) + { + Logprintf(LOG_BBS, NULL, '|', "No L4 Sessions for connect to BBS %s", user->Call); + return FALSE; + } + + SESS->HOSTSESSION->Secure_Session = 1; + } +#endif + + strcpy(conn->Callsign, user->Call); + + // Connected Event will trigger connect to remote system + + RefreshMainWindow(); + + return TRUE; + } + } + + Logprintf(LOG_BBS, NULL, '|', "No Free Streams for connect to BBS %s", user->Call); + + return FALSE; + +} + +struct DelayParam +{ + struct UserInfo * User; + CIRCUIT * conn; + int Delay; +}; + +struct DelayParam DParam; // Not 100% safe, but near enough + +VOID ConnectDelayThread(struct DelayParam * DParam) +{ + struct UserInfo * User = DParam->User; + int Delay = DParam->Delay; + + User->ForwardingInfo->Forwarding = TRUE; // Minimize window for two connects + + Sleep(Delay); + + User->ForwardingInfo->Forwarding = TRUE; + ConnecttoBBS(User); + + return; +} + +VOID ConnectPauseThread(struct DelayParam * DParam) +{ + CIRCUIT * conn = DParam->conn; + int Delay = DParam->Delay; + char Msg[] = "Pause Ok\r "; + + Sleep(Delay); + + ProcessBBSConnectScript(conn, Msg, 9); + + return; +} + + +/* +BOOL ProcessBBSConnectScriptInner(CIRCUIT * conn, char * Buffer, int len); + + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + BOOL Ret; + GetSemaphore(&ScriptSEM); + Ret = ProcessBBSConnectScriptInner(conn, Buffer, len); + FreeSemaphore(&ScriptSEM); + + return Ret; +} +*/ + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + char callsign[10]; + int port, sesstype, paclen, maxframe, l4window; + char * ptr, * ptr2; + + WriteLogLine(conn, '<',Buffer, len-1, LOG_BBS); + + Buffer[len]=0; + _strupr(Buffer); + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + if (ForwardingInfo->ScriptIndex == -1) + { + // First Entry - if first line is TIMES, check and skip forward if necessary + + int n = 0; + int Start, End; + time_t now = time(NULL), StartSecs, EndSecs; + char * Line; + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; + Line = Scripts[n]; + + if (_memicmp(Line, "TIMES", 5) == 0) + { + NextBand: + Start = atoi(&Line[6]); + End = atoi(&Line[11]); + + StartSecs = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + EndSecs = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + if ((StartSecs <= now) && (EndSecs >= now)) + goto InBand; // In band + + // Look for next TIME + NextLine: + Line = Scripts[++n]; + + if (Line == NULL) + { + // No more lines - Disconnect + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Line, "TIMES", 5) != 0) + goto NextLine; + else + goto NextBand; +InBand: + ForwardingInfo->ScriptIndex = n; + } + + } + else + { + // Dont check first time through + + if (strcmp(Buffer, "*** CONNECTED ") != 0) + { + if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + { + ForwardingInfo->MoreLines = FALSE; + } + if (!ForwardingInfo->MoreLines) + goto CheckForSID; + } + } + + if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || + (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || + strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || + strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || + strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || + strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) + { + // Connect Failed + + char * Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + int Delay = 1000; + + // Look for an alternative connect block (Starting with ELSE) + + ElseLoop: + + // Skip any comments + + while (Cmd && ((strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#'))) + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // TIMES terminates a script + + if (Cmd == 0 || _memicmp(Cmd, "TIMES", 5) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "ELSE", 4) != 0) + { + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + goto ElseLoop; + } + + if (_memicmp(&Cmd[5], "DELAY", 5) == 0) + Delay = atoi(&Cmd[10]) * 1000; + else + Delay = 1000; + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + + DParam.Delay = Delay; + DParam.User = conn->UserPointer; + + _beginthread((void (*)(void *))ConnectDelayThread, 0, &DParam); + + return FALSE; + } + + // The pointer is only updated when we get the connect, so we can tell when the last line is acked + // The first entry is always from Connected event, so don't have to worry about testing entry -1 below + + + // NETROM to KA node returns + + //c 1 milsw + //WIRAC:N9PMO-2} Connected to MILSW + //###CONNECTED TO NODE MILSW(N9ZXS) CHANNEL A + //You have reached N9ZXS's KA-Node MILSW + //ENTER COMMAND: B,C,J,N, or Help ? + + //C KB9PRF-7 + //###LINK MADE + //###CONNECTED TO NODE KB9PRF-7(KB9PRF-4) CHANNEL A + + // Look for (Space)Connected so we aren't fooled by ###CONNECTED TO NODE, which is not + // an indication of a connect. + + if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) + { + // If connected to SYNC, save IP address and port + + char * Cmd; + + if (strstr(Buffer, "*** CONNECTED TO SYNC")) + { + char * IPAddr = &Buffer[22]; + char * Port = strlop(IPAddr, ':'); + + if (Port) + { + if (conn->SyncHost) + free(conn->SyncHost); + + conn->SyncHost = _strdup(IPAddr); + conn->SyncPort = atoi(Port); + } + } + + if (conn->SkipConn) + { + conn->SkipConn = FALSE; + return TRUE; + } + + LoopBack: + + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // Only Check until script is finished + + if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) + goto LoopBack; // Blank line + + if (Cmd && _memicmp(Cmd, "TIMES", 5) != 0 && _memicmp(Cmd, "ELSE", 4) != 0) // Only Check until script is finished + { + if (_memicmp(Cmd, "MSGTYPE", 7) == 0) + { + char * ptr; + + // Select Types to send. Only send types in param. Only reverse if R in param + + _strupr(Cmd); + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + + strcpy(conn->MSGTYPES, &Cmd[8]); + + if (strchr(&Cmd[8], 'R')) conn->DoReverse = TRUE; + + ptr = strchr(&Cmd[8], 'B'); + + if (ptr) + { + conn->SendB = TRUE; + conn->MaxBLen = atoi(++ptr); + if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; + } + + ptr = strchr(&Cmd[8], 'T'); + + if (ptr) + { + conn->SendT = TRUE; + conn->MaxTLen = atoi(++ptr); + if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; + } + ptr = strchr(&Cmd[8], 'P'); + + if (ptr) + { + conn->SendP = TRUE; + conn->MaxPLen = atoi(++ptr); + if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; + } + + // If nothing to do, terminate script + + if (conn->DoReverse || SeeifMessagestoForward(conn->UserPointer->BBSNumber, conn)) + goto LoopBack; + + Logprintf(LOG_BBS, conn, '?', "Nothing to do - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "INTERLOCK ", 10) == 0) + { + // Used to limit connects on a port to 1 + + int Port; + char Option[80]; + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + sscanf(&Cmd[10], "%d %s", &Port, &Option[0]); + + if (CountConnectionsOnPort(Port)) + { + Logprintf(LOG_BBS, conn, '?', "Interlocked Port is busy - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + goto LoopBack; + } + + if (_memicmp(Cmd, "RADIO AUTH", 10) == 0) + { + // Generate a Password to enable RADIO commands on a remote node + char AuthCommand[80]; + + _strupr(Cmd); + strcpy(AuthCommand, Cmd); + + CreateOneTimePassword(&AuthCommand[11], &Cmd[11], 0); + + nodeprintf(conn, "%s\r", AuthCommand); + return TRUE; + } + + if (_memicmp(Cmd, "SKIPCON", 7) == 0) + { + // Remote Node sends Connected in CTEXT - we need to swallow it + + conn->SkipConn = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SendWL2KPM", 10) == 0|| _memicmp(Cmd, "SendWL2KFW", 10) == 0) + { + // Send ;FW: command + + conn->SendWL2KFW = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SKIPPROMPT", 10) == 0) + { + // Remote Node sends > at end of CTEXT - we need to swallow it + + conn->SkipPrompt++; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "TEXTFORWARDING", 10) == 0) + { + conn->BBSFlags |= TEXTFORWARDING; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SETCALLTOSENDER", 15) == 0) + { + conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "RADIOONLY", 9) == 0) + { + conn->BBSFlags |= WINLINKRO; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SYNC", 4) == 0) + { + conn->BBSFlags |= SYNCMODE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "NEEDLF", 6) == 0) + { + conn->BBSFlags |= NEEDLF; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "MCASTRX", 6) == 0) + { + conn->BBSFlags |= MCASTRX; + conn->MCastListenTime = atoi(&Cmd[7]) * 6; // Time to run session for *6 as value is mins put timer ticks 10 secs + + // send MCAST to Node + + nodeprintfEx(conn, "MCAST\r"); + return TRUE; + } + + if (_memicmp(Cmd, "FLARQ", 5) == 0) + { + conn->BBSFlags |= FLARQMAIL; + + CheckForEnd: + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + ForwardingInfo->MoreLines = FALSE; + + goto LoopBack; + } + if (_memicmp(Cmd, "PAUSE", 5) == 0) + { + // Pause script + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + DParam.Delay = atoi(&Cmd[6]) * 1000; + DParam.conn = conn; + + _beginthread((void (*)(void *))ConnectPauseThread, 0, &DParam); + + return TRUE; + } + + if (_memicmp(Cmd, "FILE", 4) == 0) + { + if (Cmd[4] == 0) + { + // Missing Filename + + Logprintf(LOG_BBS, conn, '!', "Export file name missing"); + } + else + ForwardMessagestoFile(conn, &Cmd[5]); + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "SMTP", 4) == 0) + { + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + + SendAMPRSMTP(conn); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + + if (_memicmp(Cmd, "IMPORT", 6) == 0) + { + char * File, * Context; + int Num; + char * Temp = _strdup(Cmd); + + File = strtok_s(&Temp[6], " ", &Context); + + if (File && File[0]) + { + Num = ImportMessages(NULL, File, TRUE); + + Logprintf(LOG_BBS, NULL, '|', "Imported %d Message(s) from %s", Num, File); + + if (Context && _stricmp(Context, "delete") == 0) + DeleteFile(File); + } + free(Temp); + + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + goto LoopBack; + } + + // Anything else is sent to Node + + // Replace \ with # so can send commands starting with # + + if (Cmd[0] == '\\') + { + Cmd[0] = '#'; + nodeprintfEx(conn, "%s\r", Cmd); + Cmd[0] = '\\'; // Put \ back in script + } + else + nodeprintfEx(conn, "%s\r", Cmd); + + return TRUE; + } + + // End of script. + + ForwardingInfo->MoreLines = FALSE; + + if (conn->BBSFlags & MCASTRX) + { + // No session with Multicast, so no SID + + conn->BBSFlags &= ~RunningConnectScript; + return TRUE; + } + + if (conn->BBSFlags & FLARQMAIL) + { + // FLARQ doesnt send a prompt - Just send message(es) + + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + // Update Paclen + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + SendARQMail(conn); + return TRUE; + } + + + return TRUE; + } + + ptr = strchr(Buffer, '}'); + + if (ptr && ForwardingInfo->MoreLines) // Beware it could be part of ctext + { + // Could be respsonse to Node Command + + ptr+=2; + + ptr2 = strchr(&ptr[0], ' '); + + if (ptr2) + { + if (_memicmp(ptr, Scripts[ForwardingInfo->ScriptIndex], ptr2-ptr) == 0) // Reply to last sscript command + { + if (Scripts[ForwardingInfo->ScriptIndex+1] && _memicmp(Scripts[ForwardingInfo->ScriptIndex+1], "else", 4) == 0) + { + // stray match or misconfigured + + return TRUE; + } + + ForwardingInfo->ScriptIndex++; + + if (Scripts[ForwardingInfo->ScriptIndex]) + if (_memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) != 0) + nodeprintf(conn, "%s\r", Scripts[ForwardingInfo->ScriptIndex]); + + return TRUE; + } + } + } + + // Not Success or Fail. If last line is still outstanding, wait fot Response + // else look for SID or Prompt + + if (conn->SkipPrompt && Buffer[len-2] == '>') + { + conn->SkipPrompt--; + return TRUE; + } + + if (ForwardingInfo->MoreLines) + return TRUE; + + // No more steps, Look for SID or Prompt + +CheckForSID: + + if (strstr(Buffer, "POSYNCHELLO")) // RMS RELAY Sync process + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + ForwardingInfo->LastReverseForward = time(NULL); + + ProcessLine(conn, 0, Buffer, len); + return FALSE; + } + + if (strstr(Buffer, "SORRY, NO")) // URONODE + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (memcmp(Buffer, ";PQ: ", 5) == 0) + { + // Secure CMS challenge + + int Len; + struct UserInfo * User = conn->UserPointer; + char * Pass = User->CMSPass; + int Response ; + char RespString[12]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + + strlop(ConnectingCall, ' '); + + if (Pass[0] == 0) + { + Pass = User->pass; // Old Way + if (Pass[0] == 0) + { + strlop(ConnectingCall, '-'); + User = LookupCall(ConnectingCall); + if (User) + Pass = User->CMSPass; + } + } + + // + + Response = GetCMSHash(&Buffer[5], Pass); + + sprintf(RespString, "%010d", Response); + + Len = sprintf(conn->SecureMsg, ";PR: %s\r", &RespString[2]); + + // Save challengs in case needed for FW lines + + strcpy(conn->PQChallenge, &Buffer[5]); + + return FALSE; + } + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // Update PACLEN + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + conn->FBBChecksum = 0; + } + + return TRUE; + } + + if (memcmp(Buffer, "[PAKET ", 7) == 0) + { + conn->BBSFlags |= BBS; + conn->BBSFlags |= MBLFORWARDING; + } + + if (Buffer[len-2] == '>') + { + if (conn->SkipPrompt) + { + conn->SkipPrompt--; + return TRUE; + } + + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + if (memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING)) + { + // PK232. Don't send a SID, and switch to Text Mode + + conn->BBSFlags |= (BBS | TEXTFORWARDING); + conn->Flags |= SENDTITLE; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + Msg = conn->FwdMsg; + + if ((conn->BBSFlags & SETCALLTOSENDER)) + nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); + else + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + return TRUE; + } + + if (strcmp(conn->Callsign, "RMS") == 0 || conn->SendWL2KFW) + { + // Build a ;FW: line with all calls with PollRMS Set + + // According to Lee if you use secure login the first + // must be the BBS call + // Actually I don't think we need the first, + // as that is implied + + // If a secure password is available send the new + // call|response format. + + // I think this should use the session callsign, which + // normally will be the BBS ApplCall, and not the BBS Name, + // but coudl be changed by *** LINKED + + int i, s; + char FWLine[10000] = ";FW: "; + struct UserInfo * user; + char RMSCall[20]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + strlop(ConnectingCall, ' '); + + strcat (FWLine, ConnectingCall); + + for (i = 0; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1; + + for (s = 0; s < 16; s++) + { + if (user->RMSSSIDBits & (1 << s)) + { + if (s) + sprintf(RMSCall, "%s-%d", user->Call, s); + else + sprintf(RMSCall, "%s", user->Call); + + // We added connectingcall at front + + if (strcmp(RMSCall, ConnectingCall) != 0) + { + strcat(FWLine, " "); + strcat(FWLine, RMSCall); + + if (user->CMSPass[0]) + { + int Response = GetCMSHash(conn->PQChallenge, user->CMSPass); + char RespString[12]; + + sprintf(RespString, "%010d", Response); + strcat(FWLine, "|"); + strcat(FWLine, &RespString[2]); + } + } + } + } + } + } + + strcat(FWLine, "\r"); + + nodeprintf(conn, FWLine); + } + + // Only declare B1 and B2 if other end did, and we are configued for it + + nodeprintfEx(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + (conn->BBSFlags & FBBCompressed) ? "B" : "", + (conn->BBSFlags & FBBB1Mode && !(conn->BBSFlags & FBBB2Mode)) ? "1" : "", + (conn->BBSFlags & FBBB2Mode) ? "2" : "", + (conn->BBSFlags & FBBForwarding) ? "F" : "", + (conn->BBSFlags & WINLINKRO) ? "" : "J"); + + if (conn->SecureMsg[0]) + { + struct UserInfo * user; + BBSputs(conn, conn->SecureMsg); + conn->SecureMsg[0] = 0; + + // Also send a Location Comment Line + + //; GM8BPQ-10 DE G8BPQ (IO92KX) + //; WL2K DE GM8BPQ () (PAT) + + user = LookupCall(BBSName); + + if (LOC && LOC[0]) + nodeprintf(conn, "; WL2K DE %s (%s)\r", BBSName, LOC); + } + + if (conn->BPQBBS && conn->MSGTYPES[0]) + + // Send a ; MSGTYPES to control what he sends us + + nodeprintf(conn, "; MSGTYPES %s\r", conn->MSGTYPES); + + if (conn->BBSFlags & FBBForwarding) + { + if (!FBBDoForward(conn)) // Send proposal if anthing to forward + { + if (conn->DoReverse) + FBBputs(conn, "FF\r"); + else + { + FBBputs(conn, "FQ\r"); + conn->CloseAfterFlush = 20; // 2 Secs + } + } + + return TRUE; + } + + return TRUE; + } + + return TRUE; +} + +VOID Parse_SID(CIRCUIT * conn, char * SID, int len) +{ + ChangeSessionIdletime(conn->BPQStream, BBSIDLETIME); // Default Idletime for BBS Sessions + + // scan backwards for first '-' + + if (strstr(SID, "BPQCHATSERVER")) + { + Disconnect(conn->BPQStream); + return; + } + + if (strstr(SID, "RMS Ex") || strstr(SID, "Winlink Ex")) + { + conn->RMSExpress = TRUE; + conn->Paclink = FALSE; + conn->PAT = FALSE; + + // Set new RMS Users as RMS User + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + + if (stristr(SID, "PAT")) + { + // Set new PAT Users as RMS User + + conn->RMSExpress = FALSE; + conn->Paclink = FALSE; + conn->PAT = TRUE; + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + if (strstr(SID, "Paclink")) + { + conn->RMSExpress = FALSE; + conn->Paclink = TRUE; + } + + if (strstr(SID, "WL2K-")) + { + conn->WL2K = TRUE; + conn->BBSFlags |= WINLINKRO; + } + + if (strstr(SID, "MFJ-")) + { + conn->BBSFlags |= MFJMODE; + } + + if (_memicmp(SID, "OpenBCM", 7) == 0) + { + // We should really only do this on Telnet Connections, as OpenBCM flag is used to remove relnet transparency + + + conn->OpenBCM = TRUE; + } + + if (_memicmp(SID, "PMS-3.2", 7) == 0) + { + // Paccom TNC that doesn't send newline prompt ater receiving subject + + conn->BBSFlags |= NEWPACCOM; + } + + // See if BPQ for selective forwarding + + if (strstr(SID, "BPQ")) + conn->BPQBBS = TRUE; + + while (len > 0) + { + switch (SID[len--]) + { + case '-': + + len=0; + break; + + case '$': + + conn->BBSFlags |= BBS | MBLFORWARDING; + conn->Paging = FALSE; + + break; + + case 'F': // FBB Blocked Forwarding + + // We now support blocked uncompressed. Not necessarily compatible with FBB + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + if (conn->UserPointer->ForwardingInfo->AllowBlocked) + { + conn->BBSFlags |= FBBForwarding | BBS; + conn->BBSFlags &= ~MBLFORWARDING; + + conn->Paging = FALSE; + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + // Allocate a Header Block + + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + } + break; + + case 'J': + + // Suspected to be associated with Winlink Radio Only + + conn->BBSFlags &= ~WINLINKRO; + break; + + case 'B': + + if (conn->UserPointer->ForwardingInfo->AllowCompressed) + { + conn->BBSFlags |= FBBCompressed; + conn->DontSaveRestartData = FALSE; // Allow restarts + + // Look for 1 or 2 or 12 as next 2 chars + + if (SID[len+2] == '1') + { + if (conn->UserPointer->ForwardingInfo->AllowB1 || + conn->UserPointer->ForwardingInfo->AllowB2) // B2 implies B1 + conn->BBSFlags |= FBBB1Mode; + + if (SID[len+3] == '2') + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + break; + } + + if (SID[len+2] == '2') + { + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + if (conn->UserPointer->ForwardingInfo->AllowB1) + conn->BBSFlags |= FBBB1Mode; // B2 should allow fallback to B1 (but RMS doesnt!) + + } + break; + } + + break; + } + } + + // Only allow blocked non-binary to other BPQ Nodes + + if ((conn->BBSFlags & FBBForwarding) && ((conn->BBSFlags & FBBCompressed) == 0) && (conn->BPQBBS == 0)) + { + // Switch back to MBL + + conn->BBSFlags |= MBLFORWARDING; + conn->BBSFlags &= ~FBBForwarding; // Turn off FBB Blocked + } + + return; +} + +VOID BBSSlowTimer() +{ + ConnectionInfo * conn; + int n; + + // Called every 10 seconds + + MCastTimer(); + + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + // Check for stuck BBS sessions (BBS session but no Node Session) + + int state; + + GetSemaphore(&ConSemaphore, 1); + SessionStateNoAck(conn->BPQStream, &state); + FreeSemaphore(&ConSemaphore); + + if (state == 0) // No Node Session + { + // is it safe just to clear Active ?? + + conn->InputMode = 0; // So Disconnect wont save partial transfer + conn->BBSFlags = 0; + Disconnected (conn->BPQStream); + continue; + } + + if (conn->BBSFlags & MCASTRX) + MCastConTimer(conn); + + + // Check SIDTImers - used to detect failure to compete SID Handshake + + if (conn->SIDResponseTimer) + { + conn->SIDResponseTimer--; + if (conn->SIDResponseTimer == 0) + { + // Disconnect Session + + Disconnect(conn->BPQStream); + } + } + } + } + + // Flush logs + + for (n = 0; n < 4; n++) + { + if (LogHandle[n]) + { + time_t LT = time(NULL); + if ((LT - LastLogTime[n]) > 30) + { + LastLogTime[n] = LT; + fclose(LogHandle[n]); + LogHandle[n] = NULL; + } + } + } +} + + +VOID FWDTimerProc() +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + ForwardingInfo->FwdTimer+=10; + + if (ForwardingInfo->FwdTimer >= ForwardingInfo->FwdInterval) + { + ForwardingInfo->FwdTimer=0; + + if (ForwardingInfo->FWDBands && ForwardingInfo->FWDBands[0]) + { + // Check Timebands + + struct FWDBAND ** Bands = ForwardingInfo->FWDBands; + int Count = 0; + time_t now = time(NULL); + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; // Secs in day + + while(Bands[Count]) + { + if ((Bands[Count]->FWDStartBand < now) && (Bands[Count]->FWDEndBand >= now)) + goto FWD; // In band + + Count++; + } + continue; // Out of bands + } + FWD: + if (ForwardingInfo->Enabled) + { + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + { + //Temp Debug Code + +// Debugprintf("ReverseFlag = %d, Msgs to Forward Flag %d Msgs to Forward Count %d", +// ForwardingInfo->ReverseFlag, +// SeeifMessagestoForward(user->BBSNumber, NULL), +// CountMessagestoForward(user)); + + if (SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) + + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + + // remove any old TempScript + + if (user->ForwardingInfo->TempConnectScript) + { + FreeList(user->ForwardingInfo->TempConnectScript); + user->ForwardingInfo->TempConnectScript = NULL; + } + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + } + } + } +} + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line) +{ + // ?? malloc and clear + + void * ptr; + +#ifdef WIN32 + ptr=_malloc_dbg(len, type, file, line); +#else + ptr = malloc(len); +#endif + if (ptr) + memset(ptr, 0, len); + + return ptr; +} + +struct MsgInfo * FindMessageByNumber(int msgno) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + if (Msg->number && Msg->number < msgno) // sometimes get zero msg number + return NULL; // Not found + + m--; + + } while (m > 0); + + return NULL; +} + +struct MsgInfo * FindMessageByBID(char * BID) +{ + int m = NumberofMessages; + + struct MsgInfo * Msg; + + while (m > 0) + { + Msg = MsgHddrPtr[m]; + + if (strcmp(Msg->bid, BID) == 0) + return Msg; + + m--; + } + + return NULL; +} + +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, j = 0, val1, val2; + unsigned char hostname[100]=""; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // String is now encoded as hex pairs, but still need to decode old format + + for (i=0; i < len; i++) + { + if (Encrypt[i] < '0' || Encrypt[i] > 'F') + goto OldFormat; + } + + // Only '0' to 'F' + + for (i=0; i < len; i++) + { + val1 = Encrypt[i++]; + val1 -= '0'; + if (val1 > 9) + val1 -= 7; + + val2 = Encrypt[i]; + val2 -= '0'; + if (val2 > 9) + val2 -= 7; + + Pass[j] = (val1 << 4) | val2; + Pass[j] ^= hash[j]; + j++; + } + + return; + +OldFormat: + + for (i=0; i < len; i++) + { + Pass[i] = Encrypt[i] ^ hash[i]; + } + + return; +} + +int EncryptPass(char * Pass, char * Encrypt) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, val; + unsigned char hostname[100]; + unsigned char extendedpass[100]; + unsigned int passlen; + unsigned char * ptr; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // if password is less than 16 chars, extend with zeros + + passlen=(int)strlen(Pass); + + strcpy(extendedpass, Pass); + + if (passlen < 16) + { + for (i=passlen+1; i <= 16; i++) + { + extendedpass[i] = 0; + } + + passlen = 16; + } + + ptr = Encrypt; + Encrypt[0] = 0; + + for (i=0; i < passlen; i++) + { + val = extendedpass[i] ^ hash[i]; + ptr += sprintf(ptr, "%02X", val); + } + + return passlen * 2; +} + + + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +VOID SaveInt64Value(config_setting_t * group, char * name, long long value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT64); + if(setting) + config_setting_set_int64(setting, value); +} + +VOID SaveFloatValue(config_setting_t * group, char * name, double value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_FLOAT); + if (setting) + config_setting_set_float(setting, value); +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + +VOID SaveOverride(config_setting_t * group, char * name, struct Override ** values) +{ + config_setting_t *setting; + struct Override ** Calls; + char Multi[10000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + ptr += sprintf(ptr, "%s, %d|", Calls[0]->Call, Calls[0]->Days); + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + + +VOID SaveMultiStringValue(config_setting_t * group, char * name, char ** values) +{ + config_setting_t *setting; + char ** Calls; + char Multi[100000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + strcpy(ptr, Calls[0]); + ptr += strlen(Calls[0]); + *(ptr++) = '|'; + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + +int configSaved = 0; + +VOID SaveConfig(char * ConfigName) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + config_setting_t *root, *group, *bbs; + int i; + char Size[80]; + struct BBSForwardingInfo DummyForwardingInfo; + char Line[1024]; + + if (configSaved == 0) + { + // only create backup once per run + + CopyConfigFile(ConfigName); + configSaved = 1; + } + + memset(&DummyForwardingInfo, 0, sizeof(struct BBSForwardingInfo)); + + // Get rid of old config before saving + + config_destroy(&cfg); + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "Streams", MaxStreams); + SaveIntValue(group, "BBSApplNum", BBSApplNum); + SaveStringValue(group, "BBSName", BBSName); + SaveStringValue(group, "SYSOPCall", SYSOPCall); + SaveStringValue(group, "H-Route", HRoute); + SaveStringValue(group, "AMPRDomain", AMPRDomain); + SaveIntValue(group, "EnableUI", EnableUI); + SaveIntValue(group, "RefuseBulls", RefuseBulls); + SaveIntValue(group, "OnlyKnown", OnlyKnown); + SaveIntValue(group, "SendSYStoSYSOPCall", SendSYStoSYSOPCall); + SaveIntValue(group, "SendBBStoSYSOPCall", SendBBStoSYSOPCall); + SaveIntValue(group, "DontHoldNewUsers", DontHoldNewUsers); + SaveIntValue(group, "DefaultNoWINLINK", DefaultNoWINLINK); + SaveIntValue(group, "AllowAnon", AllowAnon); + SaveIntValue(group, "DontNeedHomeBBS", DontNeedHomeBBS); + SaveIntValue(group, "DontCheckFromCall", DontCheckFromCall); + SaveIntValue(group, "UserCantKillT", UserCantKillT); + + SaveIntValue(group, "ForwardToMe", ForwardToMe); + SaveIntValue(group, "SMTPPort", SMTPInPort); + SaveIntValue(group, "POP3Port", POP3InPort); + SaveIntValue(group, "NNTPPort", NNTPInPort); + SaveIntValue(group, "RemoteEmail", RemoteEmail); + SaveIntValue(group, "SendAMPRDirect", SendAMPRDirect); + + SaveIntValue(group, "MailForInterval", MailForInterval); + SaveStringValue(group, "MailForText", MailForText); + + EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); + + SaveIntValue(group, "AuthenticateSMTP", SMTPAuthNeeded); + + SaveIntValue(group, "MulticastRX", MulticastRX); + + SaveIntValue(group, "SMTPGatewayEnabled", ISP_Gateway_Enabled); + SaveIntValue(group, "ISPSMTPPort", ISPSMTPPort); + SaveIntValue(group, "ISPPOP3Port", ISPPOP3Port); + SaveIntValue(group, "POP3PollingInterval", ISPPOP3Interval); + + SaveStringValue(group, "MyDomain", MyDomain); + SaveStringValue(group, "ISPSMTPName", ISPSMTPName); + SaveStringValue(group, "ISPEHLOName", ISPEHLOName); + SaveStringValue(group, "ISPPOP3Name", ISPPOP3Name); + SaveStringValue(group, "ISPAccountName", ISPAccountName); + SaveStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); + + + // Save Window Sizes + +#ifndef LINBPQ + + if (ConsoleRect.right) + { + sprintf(Size,"%d,%d,%d,%d",ConsoleRect.left, ConsoleRect.right, + ConsoleRect.top, ConsoleRect.bottom); + + SaveStringValue(group, "ConsoleSize", Size); + } + + sprintf(Size,"%d,%d,%d,%d,%d",MonitorRect.left,MonitorRect.right,MonitorRect.top,MonitorRect.bottom, hMonitor ? 1 : 0); + SaveStringValue(group, "MonitorSize", Size); + + sprintf(Size,"%d,%d,%d,%d",MainRect.left,MainRect.right,MainRect.top,MainRect.bottom); + SaveStringValue(group, "WindowSize", Size); + + SaveIntValue(group, "Bells", Bells); + SaveIntValue(group, "FlashOnBell", FlashOnBell); + SaveIntValue(group, "StripLF", StripLF); + SaveIntValue(group, "WarnWrap", WarnWrap); + SaveIntValue(group, "WrapInput", WrapInput); + SaveIntValue(group, "FlashOnConnect", FlashOnConnect); + SaveIntValue(group, "CloseWindowOnBye", CloseWindowOnBye); + +#endif + + SaveIntValue(group, "Log_BBS", LogBBS); + SaveIntValue(group, "Log_TCP", LogTCP); + + sprintf(Size,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + SaveStringValue(group, "Version", Size); + + // Save Welcome Messages and prompts + + SaveStringValue(group, "WelcomeMsg", WelcomeMsg); + SaveStringValue(group, "NewUserWelcomeMsg", NewWelcomeMsg); + SaveStringValue(group, "ExpertWelcomeMsg", ExpertWelcomeMsg); + + SaveStringValue(group, "Prompt", Prompt); + SaveStringValue(group, "NewUserPrompt", NewPrompt); + SaveStringValue(group, "ExpertPrompt", ExpertPrompt); + SaveStringValue(group, "SignoffMsg", SignoffMsg); + + SaveMultiStringValue(group, "RejFrom", RejFrom); + SaveMultiStringValue(group, "RejTo", RejTo); + SaveMultiStringValue(group, "RejAt", RejAt); + SaveMultiStringValue(group, "RejBID", RejBID); + + SaveMultiStringValue(group, "HoldFrom", HoldFrom); + SaveMultiStringValue(group, "HoldTo", HoldTo); + SaveMultiStringValue(group, "HoldAt", HoldAt); + SaveMultiStringValue(group, "HoldBID", HoldBID); + + SaveIntValue(group, "SendWP", SendWP); + SaveIntValue(group, "SendWPType", SendWPType); + SaveIntValue(group, "FilterWPBulls", FilterWPBulls); + SaveIntValue(group, "NoWPGuesses", NoWPGuesses); + + SaveStringValue(group, "SendWPTO", SendWPTO); + SaveStringValue(group, "SendWPVIA", SendWPVIA); + + SaveMultiStringValue(group, "SendWPAddrs", SendWPAddrs); + + // Save Forwarding Config + + // Interval and Max Sizes and Aliases are not user specific + + SaveIntValue(group, "MaxTXSize", MaxTXSize); + SaveIntValue(group, "MaxRXSize", MaxRXSize); + SaveIntValue(group, "ReaddressLocal", ReaddressLocal); + SaveIntValue(group, "ReaddressReceived", ReaddressReceived); + SaveIntValue(group, "WarnNoRoute", WarnNoRoute); + SaveIntValue(group, "Localtime", Localtime); + SaveIntValue(group, "SendPtoMultiple", SendPtoMultiple); + + SaveMultiStringValue(group, "FWDAliases", AliasText); + + bbs = config_setting_add(root, "BBSForwarding", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + ForwardingInfo = user->ForwardingInfo; + + if (ForwardingInfo == NULL) + continue; + + if (memcmp(ForwardingInfo, &DummyForwardingInfo, sizeof(struct BBSForwardingInfo)) == 0) + continue; // Ignore empty records; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + char Key[20] = "*"; + strcat (Key, user->Call); + group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + + SaveMultiStringValue(group, "TOCalls", ForwardingInfo->TOCalls); + SaveMultiStringValue(group, "ConnectScript", ForwardingInfo->ConnectScript); + SaveMultiStringValue(group, "ATCalls", ForwardingInfo->ATCalls); + SaveMultiStringValue(group, "HRoutes", ForwardingInfo->Haddresses); + SaveMultiStringValue(group, "HRoutesP", ForwardingInfo->HaddressesP); + SaveMultiStringValue(group, "FWDTimes", ForwardingInfo->FWDTimes); + + SaveIntValue(group, "Enabled", ForwardingInfo->Enabled); + SaveIntValue(group, "RequestReverse", ForwardingInfo->ReverseFlag); + SaveIntValue(group, "AllowBlocked", ForwardingInfo->AllowBlocked); + SaveIntValue(group, "AllowCompressed", ForwardingInfo->AllowCompressed); + SaveIntValue(group, "UseB1Protocol", ForwardingInfo->AllowB1); + SaveIntValue(group, "UseB2Protocol", ForwardingInfo->AllowB2); + SaveIntValue(group, "SendCTRLZ", ForwardingInfo->SendCTRLZ); + + SaveIntValue(group, "FWDPersonalsOnly", ForwardingInfo->PersonalOnly); + SaveIntValue(group, "FWDNewImmediately", ForwardingInfo->SendNew); + SaveIntValue(group, "FwdInterval", ForwardingInfo->FwdInterval); + SaveIntValue(group, "RevFWDInterval", ForwardingInfo->RevFwdInterval); + SaveIntValue(group, "MaxFBBBlock", ForwardingInfo->MaxFBBBlockSize); + SaveIntValue(group, "ConTimeout", ForwardingInfo->ConTimeout); + + SaveStringValue(group, "BBSHA", ForwardingInfo->BBSHA); + } + + + // Save Housekeeping config + + group = config_setting_add(root, "Housekeeping", CONFIG_TYPE_GROUP); + + SaveInt64Value(group, "LastHouseKeepingTime", LastHouseKeepingTime); + SaveInt64Value(group, "LastTrafficTime", LastTrafficTime); + SaveIntValue(group, "MaxMsgno", MaxMsgno); + SaveIntValue(group, "BidLifetime", BidLifetime); + SaveIntValue(group, "MaxAge", MaxAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "MaintInterval", MaintInterval); + SaveIntValue(group, "UserLifetime", UserLifetime); + SaveIntValue(group, "MaintTime", MaintTime); + SaveFloatValue(group, "PR", PR); + SaveFloatValue(group, "PUR", PUR); + SaveFloatValue(group, "PF", PF); + SaveFloatValue(group, "PNF", PNF); + SaveIntValue(group, "BF", BF); + SaveIntValue(group, "BNF", BNF); + SaveIntValue(group, "NTSD", NTSD); + SaveIntValue(group, "NTSF", NTSF); + SaveIntValue(group, "NTSU", NTSU); +// SaveIntValue(group, "AP", AP); +// SaveIntValue(group, "AB", AB); + SaveIntValue(group, "DeletetoRecycleBin", DeletetoRecycleBin); + SaveIntValue(group, "SuppressMaintEmail", SuppressMaintEmail); + SaveIntValue(group, "MaintSaveReg", SaveRegDuringMaint); + SaveIntValue(group, "OverrideUnsent", OverrideUnsent); + SaveIntValue(group, "SendNonDeliveryMsgs", SendNonDeliveryMsgs); + SaveIntValue(group, "GenerateTrafficReport", GenerateTrafficReport); + + SaveOverride(group, "LTFROM", LTFROM); + SaveOverride(group, "LTTO", LTTO); + SaveOverride(group, "LTAT", LTAT); + + // Save UI config + + for (i=1; i<=32; i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + if (group) + { + SaveIntValue(group, "Enabled", UIEnabled[i]); + SaveIntValue(group, "SendMF", UIMF[i]); + SaveIntValue(group, "SendHDDR", UIHDDR[i]); + SaveIntValue(group, "SendNull", UINull[i]); + + if (UIDigi[i]) + SaveStringValue(group, "Digis", UIDigi[i]); + } + } + + // Save User Config + + bbs = config_setting_add(root, "BBSUsers", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + char stats[256], stats2[256]; + struct MsgStats * Stats; + char Key[20] = "*"; + + user = UserRecPtr[i]; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + strcat (Key, user->Call); +// group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + { + strcpy(Key, user->Call); +// group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + } + /* + SaveStringValue(group, "Name", user->Name); + SaveStringValue(group, "Address", user->Address); + SaveStringValue(group, "HomeBBS", user->HomeBBS); + SaveStringValue(group, "QRA", user->QRA); + SaveStringValue(group, "pass", user->pass); + SaveStringValue(group, "ZIP", user->ZIP); + SaveStringValue(group, "CMSPass", user->CMSPass); + + SaveIntValue(group, "lastmsg", user->lastmsg); + SaveIntValue(group, "flags", user->flags); + SaveIntValue(group, "PageLen", user->PageLen); + SaveIntValue(group, "BBSNumber", user->BBSNumber); + SaveIntValue(group, "RMSSSIDBits", user->RMSSSIDBits); + SaveIntValue(group, "WebSeqNo", user->WebSeqNo); + + SaveInt64Value(group, "TimeLastConnected", user->TimeLastConnected); +*/ + Stats = &user->Total; + +// sprintf(stats, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + sprintf(stats, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Totsl", stats); + + Stats = &user->Last; + + sprintf(stats2, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", +// sprintf(stats2, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Last", stats2); + + sprintf(Line,"%s^%s^%s^%s^%s^%s^%s^%d^%d^%d^%d^%d^%d^%lld^%s^%s", + user->Name, user->Address, user->HomeBBS, user->QRA, user->pass, user->ZIP, user->CMSPass, + user->lastmsg, user->flags, user->PageLen, user->BBSNumber, user->RMSSSIDBits, user->WebSeqNo, + user->TimeLastConnected, stats, stats2); + + if (strlen(Line) < 10) + continue; + + SaveStringValue(bbs, Key, Line); + } + +/* + wp = config_setting_add(root, "WP", CONFIG_TYPE_GROUP); + + for (i = 0; i <= NumberofWPrecs; i++) + { + char WPString[1024]; + long long val1, val2; + + WP = WPRecPtr[i]; + val1 = WP->last_modif; + val2 = WP->last_seen; + + sprintf(Key, "R%d", i); + + sprintf(WPString, "%s|%s|%d|%d|%d|%s|%s|%s|%s|%s|%s|%ld|%ld", + &WP->callsign[0], &WP->name[0], WP->Type, WP->changed, WP->seen, &WP->first_homebbs[0], + &WP->secnd_homebbs[0], &WP->first_zip[0], &WP->secnd_zip[0], &WP->first_qth[0], &WP->secnd_qth[0], + val1, val2); + + SaveStringValue(wp, Key, WPString); + } + + // Save Message Headers + + msgs = config_setting_add(root, "MSGS", CONFIG_TYPE_GROUP); + + memset(MsgHddrPtr[0], 0, sizeof(struct MsgInfo)); + + MsgHddrPtr[0]->type = 'X'; + MsgHddrPtr[0]->status = '2'; + MsgHddrPtr[0]->number = 0; + MsgHddrPtr[0]->length = LatestMsg; + + + for (i = 0; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d", Msg->number); + + n = sprintf(Line, "%c|%c|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + + SaveStringValue(msgs, Key, Line); + } + + // Save Bids + + msgs = config_setting_add(root, "BIDS", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofBIDs; i++) + { + sprintf(Key, "R%s", BIDRecPtr[i]->BID); + sprintf(Line, "%d|%d", BIDRecPtr[i]->mode, BIDRecPtr[i]->u.timestamp); + SaveStringValue(msgs, Key, Line); + } + +#ifdef LINBPQ + + if(! config_write_file(&cfg,"/dev/shm/linmail.cfg.temp" )) + { + print("Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + CopyFile("/dev/shm/linmail.cfg.temp", ConfigName, FALSE); + +#else +*/ + if(! config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + +//#endif + + config_destroy(&cfg); + +/* + +#ifndef LINBPQ + + // Save a copy with current Date/Time Stamp for debugging + + { + char Backup[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(Backup,"%s.%02d%02d%02d%02d%02d.save", ConfigName, tm->tm_year-100, tm->tm_mon+1, + tm->tm_mday, tm->tm_hour, tm->tm_min); + + CopyFile(ConfigName, Backup, FALSE); // Copy to .bak + } +#endif +*/ +} + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +long long GetInt64Value(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int64 (setting); + + return 0; +} + +double GetFloatValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + + if (setting) + { + return config_setting_get_float (setting); + } + return 0; +} + +int GetIntValueWithDefault(config_setting_t * group, char * name, int Default) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return Default; +} + + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + value[0] = 0; + return FALSE; +} + +BOOL GetConfig(char * ConfigName) +{ + int i; + char Size[80]; + config_setting_t *setting; + const char * ptr; + + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + char Msg[256]; + sprintf(Msg, "Config File Line %d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); +#ifdef WIN32 + MessageBox(NULL, Msg, "BPQMail", MB_ICONSTOP); +#else + printf("%s", Msg); +#endif + config_destroy(&cfg); + return(EXIT_FAILURE); + } + +#if LIBCONFIG_VER_MINOR > 5 + config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); +#else + config_set_auto_convert (&cfg, 1); +#endif + + group = config_lookup (&cfg, "main"); + + if (group == NULL) + return EXIT_FAILURE; + + SMTPInPort = GetIntValue(group, "SMTPPort"); + POP3InPort = GetIntValue(group, "POP3Port"); + NNTPInPort = GetIntValue(group, "NNTPPort"); + RemoteEmail = GetIntValue(group, "RemoteEmail"); + MaxStreams = GetIntValue(group, "Streams"); + BBSApplNum = GetIntValue(group, "BBSApplNum"); + EnableUI = GetIntValue(group, "EnableUI"); + MailForInterval = GetIntValue(group, "MailForInterval"); + RefuseBulls = GetIntValue(group, "RefuseBulls"); + OnlyKnown = GetIntValue(group, "OnlyKnown"); + SendSYStoSYSOPCall = GetIntValue(group, "SendSYStoSYSOPCall"); + SendBBStoSYSOPCall = GetIntValue(group, "SendBBStoSYSOPCall"); + DontHoldNewUsers = GetIntValue(group, "DontHoldNewUsers"); + DefaultNoWINLINK = GetIntValue(group, "DefaultNoWINLINK"); + ForwardToMe = GetIntValue(group, "ForwardToMe"); + AllowAnon = GetIntValue(group, "AllowAnon"); + UserCantKillT = GetIntValue(group, "UserCantKillT"); + + DontNeedHomeBBS = GetIntValue(group, "DontNeedHomeBBS"); + DontCheckFromCall = GetIntValue(group, "DontCheckFromCall"); + MaxTXSize = GetIntValue(group, "MaxTXSize"); + MaxRXSize = GetIntValue(group, "MaxRXSize"); + ReaddressLocal = GetIntValue(group, "ReaddressLocal"); + ReaddressReceived = GetIntValue(group, "ReaddressReceived"); + WarnNoRoute = GetIntValue(group, "WarnNoRoute"); + SendPtoMultiple = GetIntValue(group, "SendPtoMultiple"); + Localtime = GetIntValue(group, "Localtime"); + AliasText = GetMultiStringValue(group, "FWDAliases"); + GetStringValue(group, "BBSName", BBSName); + GetStringValue(group, "MailForText", MailForText); + GetStringValue(group, "SYSOPCall", SYSOPCall); + GetStringValue(group, "H-Route", HRoute); + GetStringValue(group, "AMPRDomain", AMPRDomain); + SendAMPRDirect = GetIntValue(group, "SendAMPRDirect"); + ISP_Gateway_Enabled = GetIntValue(group, "SMTPGatewayEnabled"); + ISPPOP3Interval = GetIntValue(group, "POP3PollingInterval"); + GetStringValue(group, "MyDomain", MyDomain); + GetStringValue(group, "ISPSMTPName", ISPSMTPName); + GetStringValue(group, "ISPPOP3Name", ISPPOP3Name); + ISPSMTPPort = GetIntValue(group, "ISPSMTPPort"); + ISPPOP3Port = GetIntValue(group, "ISPPOP3Port"); + GetStringValue(group, "ISPAccountName", ISPAccountName); + GetStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); + GetStringValue(group, "ISPAccountName", ISPAccountName); + + sprintf(SignoffMsg, "73 de %s\r", BBSName); // Default + GetStringValue(group, "SignoffMsg", SignoffMsg); + + DecryptPass(EncryptedISPAccountPass, ISPAccountPass, (int)strlen(EncryptedISPAccountPass)); + + SMTPAuthNeeded = GetIntValue(group, "AuthenticateSMTP"); + LogBBS = GetIntValue(group, "Log_BBS"); + LogTCP = GetIntValue(group, "Log_TCP"); + + MulticastRX = GetIntValue(group, "MulticastRX"); + +#ifndef LINBPQ + + GetStringValue(group, "MonitorSize", Size); + sscanf(Size,"%d,%d,%d,%d,%d",&MonitorRect.left,&MonitorRect.right,&MonitorRect.top,&MonitorRect.bottom,&OpenMon); + + GetStringValue(group, "WindowSize", Size); + sscanf(Size,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); + + Bells = GetIntValue(group, "Bells"); + + FlashOnBell = GetIntValue(group, "FlashOnBell"); + + StripLF = GetIntValue(group, "StripLF"); + CloseWindowOnBye = GetIntValue(group, "CloseWindowOnBye"); + WarnWrap = GetIntValue(group, "WarnWrap"); + WrapInput = GetIntValue(group, "WrapInput"); + FlashOnConnect = GetIntValue(group, "FlashOnConnect"); + + GetStringValue(group, "ConsoleSize", Size); + sscanf(Size,"%d,%d,%d,%d,%d", &ConsoleRect.left, &ConsoleRect.right, + &ConsoleRect.top, &ConsoleRect.bottom,&OpenConsole); + +#endif + + // Get Welcome Messages + + setting = config_setting_get_member (group, "WelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + WelcomeMsg = _strdup(ptr); + } + else + WelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "NewUserWelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + NewWelcomeMsg = _strdup(ptr); + } + else + NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "ExpertWelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + ExpertWelcomeMsg = _strdup(ptr); + } + else + ExpertWelcomeMsg = _strdup(""); + + // Get Prompts + + setting = config_setting_get_member (group, "Prompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + Prompt = _strdup(ptr); + } + else + { + Prompt = malloc(20); + sprintf(Prompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "NewUserPrompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + NewPrompt = _strdup(ptr); + } + else + { + NewPrompt = malloc(20); + sprintf(NewPrompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "ExpertPrompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + ExpertPrompt = _strdup(ptr); + } + else + { + ExpertPrompt = malloc(20); + sprintf(ExpertPrompt, "de %s>\r\n", BBSName); + } + + TidyPrompts(); + + RejFrom = GetMultiStringValue(group, "RejFrom"); + RejTo = GetMultiStringValue(group, "RejTo"); + RejAt = GetMultiStringValue(group, "RejAt"); + RejBID = GetMultiStringValue(group, "RejBID"); + + HoldFrom = GetMultiStringValue(group, "HoldFrom"); + HoldTo = GetMultiStringValue(group, "HoldTo"); + HoldAt = GetMultiStringValue(group, "HoldAt"); + HoldBID = GetMultiStringValue(group, "HoldBID"); + + // Send WP Params + + SendWP = GetIntValue(group, "SendWP"); + SendWPType = GetIntValue(group, "SendWPType"); + + GetStringValue(group, "SendWPTO", SendWPTO); + GetStringValue(group, "SendWPVIA", SendWPVIA); + + SendWPAddrs = GetMultiStringValue(group, "SendWPAddrs"); + + FilterWPBulls = GetIntValue(group, "FilterWPBulls"); + NoWPGuesses = GetIntValue(group, "NoWPGuesses"); + + if (SendWPAddrs[0] == NULL && SendWPTO[0]) + { + // convert old format TO and VIA to entry in SendWPAddrs + + SendWPAddrs = realloc(SendWPAddrs, 8); // Add entry + + if (SendWPVIA[0]) + { + char WP[256]; + + sprintf(WP, "%s@%s", SendWPTO, SendWPVIA); + SendWPAddrs[0] = _strdup(WP); + } + else + SendWPAddrs[0] = _strdup(SendWPTO); + + + SendWPAddrs[1] = 0; + + SendWPTO[0] = 0; + SendWPVIA[0] = 0; + } + + GetStringValue(group, "Version", Size); + sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); + + for (i=1; i<=32; i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_lookup (&cfg, Key); + + if (group) + { + UIEnabled[i] = GetIntValue(group, "Enabled"); + UIMF[i] = GetIntValueWithDefault(group, "SendMF", UIEnabled[i]); + UIHDDR[i] = GetIntValueWithDefault(group, "SendHDDR", UIEnabled[i]); + UINull[i] = GetIntValue(group, "SendNull"); + Size[0] = 0; + GetStringValue(group, "Digis", Size); + if (Size[0]) + UIDigi[i] = _strdup(Size); + } + } + + group = config_lookup (&cfg, "Housekeeping"); + + if (group) + { + LastHouseKeepingTime = GetIntValue(group, "LastHouseKeepingTime"); + LastTrafficTime = GetIntValue(group, "LastTrafficTime"); + MaxMsgno = GetIntValue(group, "MaxMsgno"); + LogAge = GetIntValue(group, "LogLifetime"); + BidLifetime = GetIntValue(group, "BidLifetime"); + MaxAge = GetIntValue(group, "MaxAge"); + if (MaxAge == 0) + MaxAge = 30; + UserLifetime = GetIntValue(group, "UserLifetime"); + MaintInterval = GetIntValue(group, "MaintInterval"); + + if (MaintInterval == 0) + MaintInterval = 24; + + MaintTime = GetIntValue(group, "MaintTime"); + + PR = GetFloatValue(group, "PR"); + PUR = GetFloatValue(group, "PUR"); + PF = GetFloatValue(group, "PF"); + PNF = GetFloatValue(group, "PNF"); + + BF = GetIntValue(group, "BF"); + BNF = GetIntValue(group, "BNF"); + NTSD = GetIntValue(group, "NTSD"); + NTSU = GetIntValue(group, "NTSU"); + NTSF = GetIntValue(group, "NTSF"); +// AP = GetIntValue(group, "AP"); +// AB = GetIntValue(group, "AB"); + DeletetoRecycleBin = GetIntValue(group, "DeletetoRecycleBin"); + SuppressMaintEmail = GetIntValue(group, "SuppressMaintEmail"); + SaveRegDuringMaint = GetIntValue(group, "MaintSaveReg"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + SendNonDeliveryMsgs = GetIntValue(group, "SendNonDeliveryMsgs"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + GenerateTrafficReport = GetIntValueWithDefault(group, "GenerateTrafficReport", 1); + + LTFROM = GetOverrides(group, "LTFROM"); + LTTO = GetOverrides(group, "LTTO"); + LTAT = GetOverrides(group, "LTAT"); + } + + return EXIT_SUCCESS; +} + + +int Connected(int Stream) +{ + int n, Mask; + CIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + char Title[100]; + int Freq = 0; + int Mode = 0; + BPQVECSTRUC * SESS; + TRANSPORTENTRY * Sess1 = NULL, * Sess2; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + if (conn->BBSFlags & RunningConnectScript) + { + // BBS Outgoing Connect + + conn->paclen = 236; + + // Run first line of connect script + + ChangeSessionIdletime(Stream, BBSIDLETIME); // Default Idletime for BBS Sessions + ProcessBBSConnectScript(conn, ConnectedMsg, 15); + return 0; + } + } + + // Incoming Connect + + // Try to find port, freq, mode, etc + +#ifdef LINBPQ + SESS = &BPQHOSTVECTOR[0]; +#else + SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + SESS +=(Stream - 1); + + if (SESS) + Sess1 = SESS->HOSTSESSION; + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // See if L2 session - if so, get info from WL2K report line + + // if Session has report info, use it + + if (Sess2->Mode) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + else if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + Freq = PORT->WL2KInfo.Freq; + Mode = PORT->WL2KInfo.mode; + } + else + { + if (Sess2->RMSCall[0]) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + } + } + } + + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + strlop(callsign, ' '); // Remove trailing spaces + + if (strcmp(&callsign[strlen(callsign) - 2], "-T") == 0) + conn->RadioOnlyMode = 'T'; + else if (strcmp(&callsign[strlen(callsign) - 2], "-R") == 0) + conn->RadioOnlyMode = 'R'; + else + conn->RadioOnlyMode = 0; + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = LookupCall(callsign); + + if (user == NULL) + { + int Length=0; + + if (OnlyKnown) + { + // Unknown users not allowed + + n = sprintf_s(Msg, sizeof(Msg), "Incoming Connect from unknown user %s Rejected", callsign); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + Disconnect(Stream); + return 0; + } + + user = AllocateUserRecord(callsign); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (SendNewUserMessage) + { + char * MailBuffer = malloc(100); + Length += sprintf(MailBuffer, "New User %s Connected to Mailbox on Port %d Freq %d Mode %d\r\n", callsign, port, Freq, Mode); + + sprintf(Title, "New User %s", callsign); + + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + if (user == NULL) return 0; // Cant happen?? + + if (!DontHoldNewUsers) + user->flags |= F_HOLDMAIL; + + if (DefaultNoWINLINK) + user->flags |= F_NOWINLINK; + + // Always set WLE User - can't see it doing any harm + + user->flags |= F_Temp_B2_BBS; + + conn->NewUser = TRUE; + } + + user->TimeLastConnected = time(NULL); + user->Total.ConnectsIn++; + + conn->UserPointer = user; + + conn->lastmsg = user->lastmsg; + + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (paclen == 0) + { + paclen = 236; + + if (conn->SessType & Sess_PACTOR) + paclen = 100; + } + + conn->paclen = paclen; + + // Set SYSOP flag if user is defined as SYSOP and Host Session + + if (((conn->SessType & Sess_BPQHOST) == Sess_BPQHOST) && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + if (conn->Secure_Session && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + Mask = 1 << (GetApplNum(Stream) - 1); + + if (user->flags & F_Excluded) + { + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s Rejected by Exclude Flag", user->Call); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + Disconnect(Stream); + return 0; + } + + if (port) + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s on Port %d Freq %d Mode %s", + user->Call, port, Freq, WL2KModes[Mode]); + else + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt (Unless Sync) + + if (user->ForwardingInfo && user->ForwardingInfo->ConTimeout) + conn->SIDResponseTimer = user->ForwardingInfo->ConTimeout / 10; // 10 sec ticks + else + conn->SIDResponseTimer = 12; // Allow a couple of minutes for response to SID + + { + BOOL B1 = FALSE, B2 = FALSE, BIN = FALSE, BLOCKED = FALSE; + BOOL WL2KRO = FALSE; + + struct BBSForwardingInfo * ForwardingInfo; + + if (conn->RadioOnlyMode == 'R') + WL2KRO = 1; + + conn->PageLen = user->PageLen; + conn->Paging = (user->PageLen > 0); + + if (user->flags & F_Temp_B2_BBS) + { + // An RMS Express user that needs a temporary BBS struct + + if (user->ForwardingInfo == NULL) + { + // we now save the Forwarding info if BBS flag is cleared, + // so there may already be a ForwardingInfo + + user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + } + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; + + ForwardingInfo = user->ForwardingInfo; + + ForwardingInfo->AllowCompressed = TRUE; + B1 = ForwardingInfo->AllowB1 = FALSE; + B2 = ForwardingInfo->AllowB2 = TRUE; + BLOCKED = ForwardingInfo->AllowBlocked = TRUE; + } + + if (conn->NewUser) + { + BLOCKED = TRUE; + BIN = TRUE; + B2 = TRUE; + } + + if (user->ForwardingInfo) + { + BLOCKED = user->ForwardingInfo->AllowBlocked; + if (BLOCKED) + { + BIN = user->ForwardingInfo->AllowCompressed; + B1 = user->ForwardingInfo->AllowB1; + B2 = user->ForwardingInfo->AllowB2; + } + } + + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + if (conn->RadioOnlyMode) + nodeprintf(conn,";WL2K-Radio/Internet_Network\r"); + + if (!(conn->BBSFlags & SYNCMODE)) + { + + nodeprintf(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", + BLOCKED ? "FW": "", WL2KRO ? "" : "J"); + + // if (user->flags & F_Temp_B2_BBS) + // nodeprintf(conn,";PQ: 66427529\r"); + + // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); + } + } + + if ((user->Name[0] == 0) & AllowAnon) + strcpy(user->Name, user->Call); + + if (!(conn->BBSFlags & SYNCMODE)) + { + if (user->Name[0] == 0) + { + conn->Flags |= GETTINGUSER; + BBSputs(conn, NewUserPrompt); + } + else + SendWelcomeMsg(Stream, conn, user); + } + else + { + // Seems to be a timing problem - see if this fixes it + + Sleep(500); + } + + RefreshMainWindow(); + + return 0; + } + } + + return 0; +} + +int Disconnected (int Stream) +{ + struct UserInfo * user = NULL; + CIRCUIT * conn; + int n; + char Msg[255]; + int len; + char DiscMsg[] = "DISCONNECTED "; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active == FALSE) + return 0; + + // if still running connect script, reenter it to see if + // there is an else + + if (conn->BBSFlags & RunningConnectScript) + { + // We need to see if we got as far as connnected, + // as if we have we need to reset the connect script + // over the ELSE + + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + // First see if any script left + + if (Scripts[ForwardingInfo->ScriptIndex]) + { + if (ForwardingInfo->MoreLines == FALSE) + { + // Have reached end of script, so need to set back over ELSE + + ForwardingInfo->ScriptIndex--; + ForwardingInfo->MoreLines = TRUE; + } + + // if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + + + ProcessBBSConnectScript(conn, DiscMsg, 15); + return 0; + } + } + + // if sysop was chatting to user clear link +#ifndef LINBPQ + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, "User has disconnected\n", 23); + BBSConsole.Console->SysopChatStream = 0; + } +#endif + ClearQueue(conn); + + if (conn->PacLinkCalls) + free(conn->PacLinkCalls); + + if (conn->InputBuffer) + { + free(conn->InputBuffer); + conn->InputBuffer = NULL; + conn->InputBufferLen = 0; + } + + if (conn->InputMode == 'B') + { + // Save partly received message for a restart + + if (conn->BBSFlags & FBBB1Mode) + if (conn->Paclink == 0) // Paclink doesn't do restarts + if (strcmp(conn->Callsign, "RMS") != 0) // Neither does RMS Packet. + if (conn->DontSaveRestartData == FALSE) + SaveFBBBinary(conn); + } + + conn->Active = FALSE; + + if (conn->FwdMsg) + conn->FwdMsg->Locked = 0; // Unlock + + RefreshMainWindow(); + + RemoveTempBIDS(conn); + + len=sprintf_s(Msg, sizeof(Msg), "%s Disconnected", conn->Callsign); + WriteLogLine(conn, '|',Msg, len, LOG_BBS); + + if (conn->FBBHeaders) + { + struct FBBHeaderLine * FBBHeader; + int n; + + for (n = 0; n < 5; n++) + { + FBBHeader = &conn->FBBHeaders[n]; + + if (FBBHeader->FwdMsg) + FBBHeader->FwdMsg->Locked = 0; // Unlock + + } + + free(conn->FBBHeaders); + conn->FBBHeaders = NULL; + } + + if (conn->UserPointer) + { + struct BBSForwardingInfo * FWDInfo = conn->UserPointer->ForwardingInfo; + + if (FWDInfo) + { + FWDInfo->Forwarding = FALSE; + +// if (FWDInfo->UserCall[0]) // Will be set if RMS +// { +// FindNextRMSUser(FWDInfo); +// } +// else + FWDInfo->FwdTimer = 0; + } + } + + conn->BBSFlags = 0; // Clear ARQ Mode + + return 0; + } + } + return 0; +} + +int DoReceivedData(int Stream) +{ + int count, InputLen; + size_t MsgLen; + int n; + CIRCUIT * conn; + struct UserInfo * user; + char * ptr, * ptr2; + char * Buffer; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + conn->SIDResponseTimer = 0; // Got a message, so cancel timeout. + + do + { + // May have several messages per packet, or message split over packets + + OuterLoop: + if (conn->InputLen + 1000 > conn->InputBufferLen ) // Shouldnt have lines longer than this in text mode + { + conn->InputBufferLen += 1000; + conn->InputBuffer = realloc(conn->InputBuffer, conn->InputBufferLen); + } + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0 && conn->InputMode != 'Y') + return 0; + + conn->InputLen += InputLen; + + if (conn->InputLen == 0) return 0; + + conn->Watchdog = 900; // 15 Minutes + + if (conn->InputMode == 'Y') // YAPP + { + if (ProcessYAPPMessage(conn)) // Returns TRUE if there could be more to process + goto OuterLoop; + + return 0; + } + + if (conn->InputMode == 'B') + { + // if in OpenBCM mode, remove FF transparency + + if (conn->OpenBCM) // Telnet, so escape any 0xFF + { + unsigned char * ptr1 = conn->InputBuffer; + unsigned char * ptr2; + int Len; + unsigned char c; + + // We can come through here again for the + // same data as we wait for a full packet + // So only check last InputLen bytes + + ptr1 += (conn->InputLen - InputLen); + ptr2 = ptr1; + Len = InputLen; + + while (Len--) + { + c = *(ptr1++); + + if (conn->InTelnetExcape) // Last char was ff + { + conn->InTelnetExcape = FALSE; + continue; + } + + *(ptr2++) = c; + + if (c == 0xff) // + conn->InTelnetExcape = TRUE; + } + + conn->InputLen = (int)(ptr2 - conn->InputBuffer); + } + + UnpackFBBBinary(conn); + goto OuterLoop; + } + else + { + + loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + return 0; + } + + user = conn->UserPointer; + + if (conn->BBSFlags & (MCASTRX | SYNCMODE)) + { + // MCAST and SYNCMODE deliver full packets + + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + + conn->InputLen=0; + continue; + } + + // This looks for CR, CRLF, LF or CR/Null and removes any LF or NULL, + // but this relies on both arriving in same packet. + // Need to check for LF and start of packet and ignore it + // But what if client is only using LF?? + // (WLE sends SID with CRLF, other packets with CR only) + + // We don't get here on the data part of a binary transfer, so + // don't need to worry about messing up binary data. + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + ptr2 = memchr(conn->InputBuffer, '\n', conn->InputLen); + + if (ptr) + conn->usingCR = 1; + + if ((ptr2 && ptr2 < ptr) || ptr == 0) // LF before CR, or no CR + ptr = ptr2; // Use LF + + if (ptr) // CR or LF in buffer + { + conn->lastLineEnd = *(ptr); + + *(ptr) = '\r'; // In case was LF + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single msg in buffer + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (conn->InputLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + } + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (ptr2-ptr); + + Buffer = malloc(MsgLen + 100); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (MsgLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, Buffer, (int)MsgLen); + else + ProcessLine(conn, user, Buffer, (int)MsgLen); + } + free(Buffer); + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + + conn->InputLen -= (int)MsgLen; + + goto loop; + + } + } + else + { + // Could be a YAPP Header + + + if (conn->InputLen == 2 && conn->InputBuffer[0] == ENQ && conn->InputBuffer[1] == 1) // YAPP Send_Init + { + UCHAR YAPPRR[2]; + YAPPRR[0] = ACK; + YAPPRR[1] = 1; + + conn->InputMode = 'Y'; + QueueMsg(conn, YAPPRR, 2); + + conn->InputLen = 0; + return 0; + } + } + } + + } while (count > 0); + + return 0; + } + } + + // Socket not found + + return 0; + +} +int DoBBSMonitorData(int Stream) +{ +// UCHAR Buffer[1000]; + UCHAR buff[500]; + + int len = 0,count=0; + int stamp; + + do + { + stamp=GetRaw(Stream, buff,&len,&count); + + if (len == 0) return 0; + + SeeifBBSUIFrame((struct _MESSAGEX *)buff, len); + } + + while (count > 0); + + + return 0; + +} + +VOID ProcessFLARQLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) +{ + Buffer[MsgLen] = 0; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + + if (strcmp(Buffer, "ARQ::ETX\r") == 0) + { + // Decode it. + + UCHAR * ptr1, * ptr2, * ptr3; + int len, linelen; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + int LocalMsg[1000] ; // Set if Recipient is a local wl2k address + + int B2To; // Offset to To: fields in B2 header + int Recipients = 0; + int RMSMsgs = 0, BBSMsgs = 0; + +// Msg->B2Flags |= B2Msg; + + + ptr1 = conn->MailBuffer; + len = Msg->length; + ptr1[len] = 0; + + if (strstr(ptr1, "ARQ:ENCODING::")) + { + // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now + + _strupr(conn->MailBuffer); + if (strstr(conn->MailBuffer, "BBSPOLL")) + { + SendARQMail(conn); + } + + free(conn->MailBuffer); + conn->MailBuffer = NULL; + conn->MailBufferSize = 0; + + return; + } + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: + { + char SaveFrom[100]; + char * FromHA; + + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + + _strupr(FullTo); + + B2To = (int)(ptr1 - conn->MailBuffer); + + if (_memicmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + strcpy(FullTo, "RMS"); + strcpy(Msg->via, &FullTo[4]); + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + size_t Subjlen = ptr2 - &ptr1[9]; + if (Subjlen > 60) Subjlen = 60; + memcpy(Msg->title, &ptr1[9], Subjlen); + + goto ProcessBody; + } +// else if (_memicmp(ptr1, "Body:", 4) == 0) +// { +// MsgLen = atoi(&ptr1[5]); +// StartofMsg = ptr1; +// } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", + &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + Msg->datecreated = Date; + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip cr + goto Loop; + } + + + // Processed all headers +ProcessBody: + + ptr2 +=2; // skip crlf + + Msg->length = (int)(&conn->MailBuffer[Msg->length] - ptr2); + + memmove(conn->MailBuffer, ptr2, Msg->length); + + CreateMessageFromBuffer(conn); + + conn->BBSFlags = 0; // Clear ARQ Mode + return; + } + + // File away the data + + Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); + + conn->TempMsg->length += MsgLen; + + return; + + // Not sure what to do yet with files, but will process emails (using text style forwarding + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + + return; +} + +VOID ProcessTextFwdLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int len) +{ + Buffer[len] = 0; +// Debugprintf(Buffer); + + // With TNC2 body prompt is a single CR, so that shouldn't be ignored. + + // If thia causes problems with other TNC PMS implementations I'll have to revisit this + +// if (len == 1 && Buffer[0] == 13) +// return; + + if (conn->Flags & SENDTITLE) + { + // Waiting for Subject: prompt + + struct MsgInfo * Msg = conn->FwdMsg; + + nodeprintf(conn, "%s\r", Msg->title); + + conn->Flags &= ~SENDTITLE; + conn->Flags |= SENDBODY; + + // New Paccom PMS (V3.2) doesn't prompt for body so drop through and send it + if ((conn->BBSFlags & NEWPACCOM) == 0) + return; + + } + + if (conn->Flags & SENDBODY) + { + // Waiting for Enter Message Prompt + + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + char * MsgPtr; + int MsgLen; + int Index = 0; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &conn->FwdMsg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + BBSputs(conn, "\r"); + + MsgLen = RemoveLF(MsgPtr, MsgLen); + + QueueMsg(conn, MsgPtr, MsgLen); + + if (user->ForwardingInfo->SendCTRLZ) + nodeprintf(conn, "\r\x1a"); + else + nodeprintf(conn, "\r/ex\r"); + + free(MsgBytes); + + conn->FBBMsgsSent = TRUE; + + + if (conn->FwdMsg->type == 'P') + Index = PMSG; + else if (conn->FwdMsg->type == 'B') + Index = BMSG; + else if (conn->FwdMsg->type == 'T') + Index = TMSG; + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + conn->Flags &= ~SENDBODY; + conn->Flags |= WAITPROMPT; + + return; + } + + if (conn->Flags & WAITPROMPT) + { + if (Buffer[len-2] != '>') + return; + + conn->Flags &= ~WAITPROMPT; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + SaveMessageDatabase(); + + conn->UserPointer->ForwardingInfo->MsgCount--; + + // See if any more to forward + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // If we are using SETCALLTOSENDER make sure this message is from the same sender + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + unsigned char AXCall[7]; + + Msg = conn->FwdMsg; + ConvToAX25(Msg->from, AXCall); + if (memcmp(SESS[conn->BPQStream - 1].HOSTSESSION->L4USER, AXCall, 7) != 0) + { + Disconnect(conn->BPQStream); + return; + } + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + conn->Flags |= SENDTITLE; + + + if ((conn->BBSFlags & SETCALLTOSENDER)) + nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); + else + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + Disconnect(conn->BPQStream); + } + return; + } +} + + +#define N 2048 /* buffer size */ +#define F 60 /* lookahead buffer size */ +#define THRESHOLD 2 +#define NIL N /* leaf of tree */ + +extern UCHAR * infile; + +BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen); + + +VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + char * Cmd, * Arg1; + char * Context; + char seps[] = " \t\r"; + int CmdLen; + + if (_memicmp(Buffer, "POSYNCLOGON", 11) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + Sleep(500); + + BBSputs(conn, "OK\r"); + Flush(conn); + return; + } + + if (_memicmp(Buffer, "POSYNCHELLO", 11) == 0) + { + // This is first message received after connecting to SYNC + // Save Callsign + + char Reply[32]; + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + sprintf(Reply, "POSYNCLOGON %s\r", BBSName); + BBSputs(conn, Reply); + return; + } + + if (conn->BBSFlags & SYNCMODE) + { + ProcessSyncModeMessage(conn, user, Buffer, len); + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // A few messages should be trapped here and result in an immediate disconnect, whatever mode I think the session is in (it could be wrong) + + // *** Protocol Error + // Already Connected + // Invalid Command + + if (_memicmp(Buffer, "Already Connected", 17) == 0 || + _memicmp(Buffer, "Invalid Command", 15) == 0 || + _memicmp(Buffer, "*** Protocol Error", 18) == 0) + { + conn->BBSFlags |= DISCONNECTING; + Disconnect(conn->BPQStream); + return; + } + + if (conn->BBSFlags & FBBForwarding) + { + ProcessFBBLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & FLARQMODE) + { + ProcessFLARQLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MCASTRX) + { + ProcessMCASTLine(conn, user, Buffer, len); + return; + } + + + if (conn->BBSFlags & TEXTFORWARDING) + { + ProcessTextFwdLine(conn, user, Buffer, len); + return; + } + + // if chatting to sysop pass message to BBS console + + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, Buffer,len); + return; + } + + if (conn->Flags & GETTINGMESSAGE) + { + ProcessMsgLine(conn, user, Buffer, len); + return; + } + if (conn->Flags & GETTINGTITLE) + { + ProcessMsgTitle(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MBLFORWARDING) + { + ProcessMBLLine(conn, user, Buffer, len); + return; + } + + if (conn->Flags & GETTINGUSER || conn->NewUser) // Could be new user but dont need name + { + if (memcmp(Buffer, ";FW:", 4) == 0 || Buffer[0] == '[') + { + struct BBSForwardingInfo * ForwardingInfo; + + conn->Flags &= ~GETTINGUSER; + + // New User is a BBS - create a temp struct for it + + if ((user->flags & (F_BBS | F_Temp_B2_BBS)) == 0) // It could already be a BBS without a user name + { + // Not defined as BBS - allocate and initialise forwarding structure + + user->flags |= F_Temp_B2_BBS; + + // An RMS Express user that needs a temporary BBS struct + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + ForwardingInfo->AllowCompressed = TRUE; + ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->ForwardingInfo->AllowB2 = TRUE; + } + SaveUserDatabase(); + } + else + { + if (conn->Flags & GETTINGUSER) + { + conn->Flags &= ~GETTINGUSER; + if (len > 18) + len = 18; + + memcpy(user->Name, Buffer, len-1); + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + UpdateWPWithUserInfo(user); + return; + } + } + } + + // Process Command + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + { + // Waiting for paging prompt + + if (len > 1) + { + if (_memicmp(Buffer, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + } + + conn->LinesSent = 0; + return; + } + + if (user->Temp->ListSuspended) + { + // Paging limit hit when listing. User may abort, continue, or read one or more messages + + ProcessSuspendedListCommand(conn, user, Buffer, len); + return; + } + if (len == 1) + { + SendPrompt(conn, user); + return; + } + + Buffer[len] = 0; + + if (strstr(Buffer, "ARQ:FILE:")) + { + // Message from FLARQ + + conn->BBSFlags |= FLARQMODE; + strcpy(conn->ARQFilename, &Buffer[10]); // Will need name when we decide what to do with files + + // Create a Temp Messge Stucture + + CreateMessage(conn, conn->Callsign, "", "", 'P', NULL, NULL); + + Buffer[len++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + return; + } + if (Buffer[0] == ';') // WL2K Comment + { + if (memcmp(Buffer, ";FW:", 4) == 0) + { + // Paclink User Select (poll for list) + + char * ptr1,* ptr2, * ptr3; + int index=0; + + // Convert string to Multistring + + Buffer[len-1] = 0; + + conn->PacLinkCalls = zalloc(len*3); + + ptr1 = &Buffer[5]; + ptr2 = (char *)conn->PacLinkCalls; + ptr2 += (len * 2); + strcpy(ptr2, ptr1); + + while (ptr2) + { + ptr3 = strlop(ptr2, ' '); + + if (strlen(ptr2)) + conn->PacLinkCalls[index++] = ptr2; + + ptr2 = ptr3; + } + + return; + } + + if (memcmp(Buffer, ";FR:", 4) == 0) + { + // New Message from TriMode - Just igonre till I know what to do with it + + return; + } + + // Ignore other ';' message + + return; + } + + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // If a BBS, set BBS Flag + + if (user->flags & ( F_BBS | F_Temp_B2_BBS)) + { + if (user->ForwardingInfo) + { + if (user->ForwardingInfo->Forwarding && ((conn->BBSFlags & OUTWARDCONNECT) == 0)) + { + BBSputs(conn, "Already Connected\r"); + Flush(conn); + Sleep(500); + Disconnect(conn->BPQStream); + return; + } + } + + if (user->ForwardingInfo) + { + user->ForwardingInfo->Forwarding = TRUE; + user->ForwardingInfo->FwdTimer = 0; // So we dont send to immediately + } + } + + if (user->flags & ( F_BBS | F_PMS | F_Temp_B2_BBS)) + { + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + conn->FBBChecksum = 0; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + } + else + FBBputs(conn, ">\r"); + + } + + return; + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + return; + } + + Arg1 = strtok_s(NULL, seps, &Context); + CmdLen = (int)strlen(Cmd); + + // Check List first. If any other, save last listed to user record. + + if (_memicmp(Cmd, "L", 1) == 0 && _memicmp(Cmd, "LISTFILES", 3) != 0) + { + DoListCommand(conn, user, Cmd, Arg1, FALSE, Context); + SendPrompt(conn, user); + return; + } + + if (conn->lastmsg > user->lastmsg) + { + user->lastmsg = conn->lastmsg; + SaveUserDatabase(); + } + + if (_stricmp(Cmd, "SHOWRMSPOLL") == 0) + { + DoShowRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "AUTH") == 0) + { + DoAuthCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + if (_memicmp(Cmd, "Bye", CmdLen) == 0 || _stricmp(Cmd, "ELSE") == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + Disconnect(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + + if (_memicmp(Cmd, "Node", 4) == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + ReturntoNode(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + + if (_memicmp(Cmd, "IDLETIME", 4) == 0) + { + DoSetIdleTime(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "SETNEXTMESSAGENUMBER") == 0) + { + DoSetMsgNo(conn, user, Arg1, Context); + return; + } + + if (strlen(Cmd) < 12 && _memicmp(Cmd, "D", 1) == 0) + { + DoDeliveredCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "K", 1) == 0) + { + DoKillCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "LISTFILES", 3) == 0 || _memicmp(Cmd, "FILES", 5) == 0) + { + ListFiles(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "READFILE", 4) == 0) + { + ReadBBSFile(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "REROUTEMSGS", 7) == 0) + { + if (conn->sysop == 0) + nodeprintf(conn, "Reroute Messages needs SYSOP status\r"); + else + { + ReRouteMessages(); + nodeprintf(conn, "Ok\r"); + } + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "YAPP", 4) == 0) + { + YAPPSendFile(conn, user, Arg1); + return; + } + + if (_memicmp(Cmd, "UH", 2) == 0 && conn->sysop) + { + DoUnholdCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_stricmp(Cmd, "IMPORT") == 0) + { + DoImportCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "EXPORT") == 0) + { + DoExportCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "I", 1) == 0) + { + char * Save; + char * MsgBytes; + + if (Arg1) + { + // User WP lookup + + DoWPLookup(conn, user, Cmd[1], Arg1); + SendPrompt(conn, user); + return; + } + + + MsgBytes = Save = ReadInfoFile("info.txt"); + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + BBSputs(conn, "SYSOP has not created an INFO file\r"); + + + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "Name", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 17) + Arg1[17] = 0; + + strcpy(user->Name, Arg1); + UpdateWPWithUserInfo(user); + + } + + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "OP", 2) == 0) + { + int Lines; + + // Paging Control. Param is number of lines per page + + if (Arg1) + { + Lines = atoi(Arg1); + + if (Lines) // Sanity Check + { + if (Lines < 10) + { + nodeprintf(conn,"Page Length %d is too short\r", Lines); + SendPrompt(conn, user); + return; + } + } + + user->PageLen = Lines; + conn->PageLen = Lines; + conn->Paging = (Lines > 0); + SaveUserDatabase(); + } + + nodeprintf(conn,"Page Length is %d\r", user->PageLen); + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "QTH", CmdLen) == 0) + { + if (Arg1) + { + // QTH may contain spaces, so put back together, and just split at cr + + Arg1[strlen(Arg1)] = ' '; + strtok_s(Arg1, "\r", &Context); + + if (strlen(Arg1) > 60) + Arg1[60] = 0; + + strcpy(user->Address, Arg1); + UpdateWPWithUserInfo(user); + + } + + nodeprintf(conn,"QTH is %s\r", user->Address); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "ZIP", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 8) + Arg1[8] = 0; + + strcpy(user->ZIP, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + } + + nodeprintf(conn,"ZIP is %s\r", user->ZIP); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "CMSPASS", 7) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 15) + Arg1[15] = 0; + + strcpy(user->CMSPass, Arg1); + nodeprintf(conn,"CMS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "PASS", CmdLen) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 12) + Arg1[12] = 0; + + strcpy(user->pass, Arg1); + nodeprintf(conn,"BBS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + + if (_memicmp(Cmd, "R", 1) == 0) + { + DoReadCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "S", 1) == 0) + { + if (!DoSendCommand(conn, user, Cmd, Arg1, Context)) + SendPrompt(conn, user); + return; + } + + if ((_memicmp(Cmd, "Help", CmdLen) == 0) || (_memicmp(Cmd, "?", 1) == 0)) + { + char * Save; + char * MsgBytes = Save = ReadInfoFile("help.txt"); + + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + { + BBSputs(conn, "A - Abort Output\r"); + BBSputs(conn, "B - Logoff\r"); + BBSputs(conn, "CMSPASS Password - Set CMS Password\r"); + BBSputs(conn, "D - Flag NTS Message(s) as Delivered - D num\r"); + BBSputs(conn, "HOMEBBS - Display or get HomeBBS\r"); + BBSputs(conn, "INFO - Display information about this BBS\r"); + BBSputs(conn, "I CALL - Lookup CALL in WP Allows *CALL CALL* *CALL* wildcards\r"); + BBSputs(conn, "I@ PARAM - Lookup @BBS in WP\r"); + BBSputs(conn, "IZ PARAM - Lookup Zip Codes in WP\r"); + BBSputs(conn, "IH PARAM - Lookup HA elements in WP - eg USA EU etc\r"); + + BBSputs(conn, "K - Kill Message(s) - K num, KM (Kill my read messages)\r"); + BBSputs(conn, "L - List Message(s) - \r"); + BBSputs(conn, " L = List New, LR = List New (Oldest first)\r"); + BBSputs(conn, " LM = List Mine, L> Call, L< Call, L@ = List to, from or at\r"); + BBSputs(conn, " LL num = List msg num, L num-num = List Range\r"); + BBSputs(conn, " LN LY LH LK LF L$ LD = List Message with corresponding Status\r"); + BBSputs(conn, " LB LP LT = List Mesaage with corresponding Type\r"); + BBSputs(conn, " LC = List TO fields of all active bulletins\r"); + BBSputs(conn, " You can combine most selections eg LMP, LMN LB< G8BPQ\r"); + BBSputs(conn, "LISTFILES or FILES - List files available for download\r"); + + BBSputs(conn, "N Name - Set Name\r"); + BBSputs(conn, "NODE - Return to Node\r"); + BBSputs(conn, "OP n - Set Page Length (Output will pause every n lines)\r"); + BBSputs(conn, "PASS Password - Set BBS Password\r"); + BBSputs(conn, "POLLRMS - Manage Polling for messages from RMS \r"); + BBSputs(conn, "Q QTH - Set QTH\r"); + BBSputs(conn, "R - Read Message(s) - R num \r"); + BBSputs(conn, " RM (Read new messages to me), RMR (RM oldest first)\r"); + BBSputs(conn, "READ Name - Read File\r"); + + BBSputs(conn, "S - Send Message - S or SP Send Personal, SB Send Bull, ST Send NTS,\r"); + BBSputs(conn, " SR Num - Send Reply, SC Num - Send Copy\r"); + BBSputs(conn, "X - Toggle Expert Mode\r"); + BBSputs(conn, "YAPP - Download file from BBS using YAPP protocol\r"); + if (conn->sysop) + { + BBSputs(conn, "DOHOUSEKEEPING - Run Housekeeping process\r"); + BBSputs(conn, "EU - Edit User Flags - Type EU for Help\r"); + BBSputs(conn, "EXPORT - Export messages to file - Type EXPORT for Help\r"); + BBSputs(conn, "FWD - Control Forwarding - Type FWD for Help\r"); + BBSputs(conn, "IMPORT - Import messages from file - Type IMPORT for Help\r"); + BBSputs(conn, "REROUTEMSGS - Rerun message routing process\r"); + BBSputs(conn, "SETNEXTMESSAGENUMBER - Sets next message number\r"); + BBSputs(conn, "SHOWRMSPOLL - Displays your RMS polling list\r"); + BBSputs(conn, "UH - Unhold Message(s) - UH ALL or UH num num num...\r"); + } + } + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "Ver", CmdLen) == 0) + { + nodeprintf(conn, "BBS Version %s\rNode Version %s\r", VersionStringWithBuild, GetVersionString()); + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "HOMEBBS", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 40) Arg1[40] = 0; + + strcpy(user->HomeBBS, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + + if (!strchr(Arg1, '.')) + BBSputs(conn, "Please enter HA with HomeBBS eg g8bpq.gbr.eu - this will help message routing\r"); + } + + nodeprintf(conn,"HomeBBS is %s\r", user->HomeBBS); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if ((_memicmp(Cmd, "EDITUSER", 5) == 0) || (_memicmp(Cmd, "EU", 2) == 0)) + { + DoEditUserCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "POLLRMS") == 0) + { + DoPollRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "DOHOUSEKEEPING") == 0) + { + DoHousekeepingCmd(conn, user, Arg1, Context); + return; + } + + + if (_stricmp(Cmd, "FWD") == 0) + { + DoFwdCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "X", 1) == 0) + { + user->flags ^= F_Expert; + + if (user->flags & F_Expert) + BBSputs(conn, "Expert Mode\r"); + else + BBSputs(conn, "Expert Mode off\r"); + + SaveUserDatabase(); + SendPrompt(conn, user); + return; + } + + if (conn->Flags == 0) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + } + + // Send if possible + + Flush(conn); +} + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...) +{ + // seems to be printf to a socket + + char buff[600]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + BBSputs(conn, buff); +} + +// Code to delete obsolete files from Mail folder + +#ifdef WIN32 + +int DeleteRedundantMessages() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + int Msgno; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, MailDir); + strcat(szDir, "\\*.mes"); + + + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + return 0; + } + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + Msgno = atoi(&ffd.cFileName[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "%s/%s%c", MailDir, ffd.cFileName, 0); + Debugprintf("Tidy Mail - Delete %s\n", File); + +// if (DeletetoRecycleBin) + DeletetoRecycle(File); +// else +// DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return 0; +} + +#else + +#include + +int MsgFilter(const struct dirent * dir) +{ + return (strstr(dir->d_name, ".mes") != 0); +} + +int DeleteRedundantMessages() +{ + struct dirent **namelist; + int n; + struct stat STAT; + int Msgno = 0, res; + char File[100]; + + n = scandir("Mail", &namelist, MsgFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + if (stat(namelist[n]->d_name, &STAT) == 0); + { + Msgno = atoi(&namelist[n]->d_name[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "Mail/%s", namelist[n]->d_name); + printf("Deleting %s\n", File); + unlink(File); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + +VOID TidyWelcomeMsg(char ** pPrompt) +{ + // Make sure Welcome Message doesn't ends with > + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + while (i >= 0 && Prompt[i] == '>') + Prompt[i--] = 0; + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompt(char ** pPrompt) +{ + // Make sure prompt ends > CR LF + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + if (Prompt[i] != '>') + strcat(Prompt, ">"); + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompts() +{ + TidyPrompt(&Prompt); + TidyPrompt(&NewPrompt); + TidyPrompt(&ExpertPrompt); +} + +BOOL SendARQMail(CIRCUIT * conn) +{ + conn->NextMessagetoForward = FirstMessageIndextoForward; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + char MsgHddr[512]; + int HddrLen; + char TimeString[64]; + char * WholeMessage; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + Msg = conn->FwdMsg; + WholeMessage = malloc(Msg->length + 512); + + FormatTime(TimeString, (time_t)Msg->datecreated); + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + Logprintf(LOG_BBS, conn, '>', "ARQ Send Msg %d From %s To %s", Msg->number, Msg->from, Msg->to); + + HddrLen = sprintf(MsgHddr, "Date: %s\nTo: %s\nFrom: %s\nSubject %s\n\n", + TimeString, Msg->to, Msg->from, Msg->title); + + MsgLen = sprintf(WholeMessage, "ARQ:FILE::Msg%s_%d\nARQ:EMAIL::\nARQ:SIZE::%d\nARQ::STX\n%s%s\nARQ::ETX\n", + BBSName, Msg->number, (int)(HddrLen + strlen(MsgBytes)), MsgHddr, MsgBytes); + + WholeMessage[MsgLen] = 0; + QueueMsg(conn,WholeMessage, MsgLen); + + free(WholeMessage); + free(MsgBytes); + + // FLARQ doesn't ACK the message, so set flag to look for all acked + + conn->BBSFlags |= ARQMAILACK; + conn->ARQClearCount = 10; // To make sure clear isn't reported too soon + + return TRUE; + } + + // Nothing to send - close + + Logprintf(LOG_BBS, conn, '>', "ARQ Send - Nothing to Send - Closing"); + + conn->CloseAfterFlush = 20; + return FALSE; +} + +char *stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup (ch1); + chN2 = _strdup (ch2); + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + chNdx = chN2; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + + chNdx = strstr (chN1, chN2); + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + free (chN1); + free (chN2); + return chRet; +} + +#ifdef WIN32 + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\*.*"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + nodeprintf(conn, "No Files\r"); + return; + } + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + nodeprintf(conn, "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); +} + +#else + +#include + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + struct dirent **namelist; + int n, i; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + nodeprintf(conn, "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + return; +} +#endif + +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + char * MsgBytes; + + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Missing Filename\r"); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(MsgBytes); + + nodeprintf(conn, "\r\r[End of File %s]\r", filename); + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); +} + +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + struct TempUserInfo * Temp = user->Temp; + + Buffer[len] = 0; + + // Command entered during listing pause. May be A R or C (or ) + + if (Buffer[0] == 'A' || Buffer[0] == 'a') + { + // Abort + + Temp->ListActive = Temp->ListSuspended = FALSE; + SendPrompt(conn, user); + return; + } + + if (_memicmp(Buffer, "R ", 2) == 0) + { + // Read Message(es) + + int msgno; + char * ptr; + char * Context; + + ptr = strtok_s(&Buffer[2], " ", &Context); + + while (ptr) + { + msgno = atoi(ptr); + ReadMessage(conn, user, msgno); + + ptr = strtok_s(NULL, " ", &Context); + } + + nodeprintf(conn, "bort, , = Continue..>"); + return; + } + + if (Buffer[0] == 'C' || Buffer[0] == 'c' || Buffer[0] == '\r' ) + { + // Resume Listing from where we left off + + DoListCommand(conn, user, Temp->LastListCommand, Temp->LastListParams, TRUE, ""); + SendPrompt(conn, user); + return; + } + + nodeprintf(conn, "bort, , = Continue..>"); + +} +/* +CreateMessageWithAttachments() +{ + int i; + char * ptr, * ptr2, * ptr3, * ptr4; + char Boundary[1000]; + BOOL Multipart = FALSE; + BOOL ALT = FALSE; + int Partlen; + char * Save; + BOOL Base64 = FALSE; + BOOL QuotedP = FALSE; + + char FileName[100][250] = {""}; + int FileLen[100]; + char * FileBody[100]; + char * MallocSave[100]; + UCHAR * NewMsg; + + int Files = 0; + + ptr = Msg; + + if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 2000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sockptr->socket, 0); + return FALSE; + } + } + + + NewMsg = sockptr->MailBuffer + 1000; + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]); + + for (i = 1; i < Files; i++) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + + for (i = 0; i < Files; i++) + { + memcpy(NewMsg, FileBody[i], FileLen[i]); + NewMsg += FileLen[i]; + free(MallocSave[i]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + *MsgLen = NewMsg - (sockptr->MailBuffer + 1000); + *Body = sockptr->MailBuffer + 1000; + + return TRUE; // B2 Message +} + +*/ +VOID CreateUserReport() +{ + struct UserInfo * User; + int i; + char Line[200]; + int len; + char File[MAX_PATH]; + FILE * hFile; + + sprintf(File, "%s/UserList.csv", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + { + Debugprintf("Failed to create UserList.csv"); + return; + } + + for (i=1; i <= NumberofUsers; i++) + { + User = UserRecPtr[i]; + + len = sprintf(Line, "%s,%d,%s,%x,%s,\"%s\",%x,%s,%s,%s\r\n", + User->Call, + User->lastmsg, + FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), + User->flags, + User->Name, + User->Address, + User->RMSSSIDBits, + User->HomeBBS, + User->QRA, + User->ZIP +// struct MsgStats Total; +// struct MsgStats Last; + ); + fwrite(Line, 1, len, hFile); + } + + fclose(hFile); +} + +BOOL ProcessYAPPMessage(CIRCUIT * conn) +{ + int Len = conn->InputLen; + UCHAR * Msg = conn->InputBuffer; + int pktLen = Msg[1]; + char Reply[2] = {ACK}; + int NameLen, SizeLen, OptLen; + char * ptr; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char Mess[255]; + int len; + char * FN = &Msg[2]; + + switch (Msg[0]) + { + case ENQ: // YAPP Send_Init + + // Shouldn't occur in session. Reset state + + Mess[0] = ACK; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + conn->InputLen = 0; + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + return TRUE; + + case SOH: + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + // YAPPC has date/time in dos format + + if (Len < Msg[1] + 1) + return 0; + + NameLen = (int)strlen(FN); + strcpy(conn->ARQFilename, FN); + ptr = &Msg[3 + NameLen]; + SizeLen = (int)strlen(ptr); + FileSize = atoi(ptr); + + // Check file name for unsafe characters (.. / \) + + if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Mess[0] = NAK; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP File Name %s invalid\r", FN); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + OptLen = pktLen - (NameLen + SizeLen + 2); + + conn->YAPPDate = 0; + + if (OptLen >= 8) // We have a Date/Time for YAPPC + { + ptr = ptr + SizeLen + 1; + conn->YAPPDate = strtol(ptr, NULL, 16); + } + + // Check Size + + if (FileSize > MaxRXSize) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + // Make sure file does not exist + + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s already exists\r", conn->ARQFilename);; + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s already exists\r", conn->ARQFilename); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + fclose(hFile); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + + conn->MailBufferSize = FileSize; + conn->MailBuffer=malloc(FileSize); + conn->YAPPLen = 0; + + if (conn->YAPPDate) // If present use YAPPC + Reply[1] = ACK; //Receive_TPK + else + Reply[1] = 2; //Rcv_File + + QueueMsg(conn, Reply, 2); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP upload to %s started", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len, LOG_BBS); + + conn->InputLen = 0; + return FALSE; + + case STX: + + // Data Packet + + // Check we have it all + + if (conn->YAPPDate) // If present use YAPPC so have checksum + { + if (pktLen > (Len - 3)) // -3 for header and checksum + return 0; // Wait for rest + } + else + { + if (pktLen > (Len - 2)) // -2 for header + return 0; // Wait for rest + } + + // Save data and remove from buffer + + // if YAPPC check checksum + + if (conn->YAPPDate) + { + UCHAR Sum = 0; + int i; + UCHAR * uptr = &Msg[2]; + + i = pktLen; + + while(i--) + Sum += *(uptr++); + + if (Sum != *uptr) + { + // Checksum Error + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPPC Checksum Error\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + } + + if ((conn->YAPPLen) + pktLen > conn->MailBufferSize) + { + // Too Big ?? + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP Too much data received\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + + + memcpy(&conn->MailBuffer[conn->YAPPLen], &Msg[2], pktLen); + conn->YAPPLen += pktLen; + + if (conn->YAPPDate) + ++pktLen; // Add Checksum + + conn->InputLen -= (pktLen + 2); + memmove(conn->InputBuffer, &conn->InputBuffer[pktLen + 2], conn->InputLen); + + return TRUE; + + case ETX: + + // End Data + + + + if (conn->YAPPLen == conn->MailBufferSize) + { + // All received + + int ret; + DWORD Written = 0; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + +#ifdef WIN32 + hFile = CreateFile(MsgFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + ret = WriteFile(hFile, conn->MailBuffer, conn->YAPPLen, &Written, NULL); + + if (conn->YAPPDate) + { + FILETIME FileTime; + struct tm TM; + struct timeval times[2]; + time_t TT; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + TT = mktime(&TM); + times[0].tv_sec = times[1].tv_sec = + times[0].tv_usec = times[1].tv_usec = 0; + + DosDateTimeToFileTime((WORD)(conn->YAPPDate >> 16), (WORD)conn->YAPPDate & 0xFFFF, &FileTime); + ret = SetFileTime(hFile, &FileTime, &FileTime, &FileTime); + ret = GetLastError(); + + } + CloseHandle(hFile); + } +#else + + hFile = fopen(MsgFile, "wb"); + if (hFile) + { + Written = fwrite(conn->MailBuffer, 1, conn->YAPPLen, hFile); + fclose(hFile); + + if (conn->YAPPDate) + { + struct tm TM; + struct timeval times[2]; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + times[0].tv_sec = times[1].tv_sec = mktime(&TM); + times[0].tv_usec = times[1].tv_usec = 0; + } + } +#endif + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (Written != conn->YAPPLen) + { + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "Failed to save YAPP File\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + } + } + + Reply[1] = 3; //Ack_EOF + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + + return TRUE; + + case EOT: + + // End Session + + Reply[1] = 4; // Ack_EOT + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + conn->InputMode = 0; + + len = sprintf_s(Mess, sizeof(Mess), "YAPP file %s received\r", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + + return TRUE; + + case CAN: + + // Abort + + Mess[0] = ACK; + Mess[1] = 5; // CAN Ack + QueueMsg(conn, Mess, 2); + Flush(conn); + + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + + // There may be a reason after the CAN + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + + len = sprintf_s(Mess, sizeof(Mess), "YAPP Transfer cancelled by Terminal\r"); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + return FALSE; + + case ACK: + + switch (Msg[1]) + { + case 1: // Rcv_Rdy + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + len = (int)strlen(conn->ARQFilename) + 3; + + strcpy(&Mess[2], conn->ARQFilename); + len += sprintf(&Mess[len], "%d", conn->MailBufferSize); + len++; // include null + Mess[0] = SOH; + Mess[1] = len - 2; + + QueueMsg(conn, Mess, len); + Flush(conn); + conn->InputLen = 0; + + return FALSE; + + case 2: + + // Start sending message + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + case 3: + + // ACK EOF - Send EOT + + + Mess[0] = EOT; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputLen = 0; + return FALSE; + + case 4: + + // ACK EOT + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + conn->InputLen = 0; + return FALSE; + + default: + conn->InputLen = 0; + return FALSE; + + + + } + + case NAK: + + // Either Reject or Restart + + // RE Resume NAK len R NULL (File size in ASCII) NULL + + if (conn->InputLen > 2 && Msg[2] == 'R' && Msg[3] == 0) + { + int posn = atoi(&Msg[4]); + + conn->YAPPLen += posn; + conn->MailBufferSize -= posn; + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + } + + // There may be a reason after the ack + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + return FALSE; + } + + nodeprintf(conn, "Unexpected message during YAPP Transfer. Transfer canncelled\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + + return FALSE; + +} + +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Filename missing\r"); + SendPrompt(conn, user); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + SendPrompt(conn, user); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + char Mess[255]; + strcpy(conn->ARQFilename, filename); + conn->MailBuffer = malloc(FileSize); + conn->MailBufferSize = FileSize; + conn->YAPPLen = 0; + fread(conn->MailBuffer, 1, FileSize, hFile); + fclose(hFile); + + Mess[0] = ENQ; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputMode = 'Y'; + + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); + SendPrompt(conn, user); +} + +void YAPPSendData(ConnectionInfo * conn) +{ + char Mess[258]; + + conn->BBSFlags |= YAPPTX; + + while (TXCount(conn->BPQStream) < 15) + { + int Left = conn->MailBufferSize; + + if (Left == 0) + { + // Finished - send End Data + + Mess[0] = ETX; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->BBSFlags &= ~YAPPTX; + break; + } + + if (Left > conn->paclen - 2) // 2 byte header + Left = conn->paclen -2; + + memcpy(&Mess[2], &conn->MailBuffer[conn->YAPPLen], Left); + Mess[0] = STX; + Mess[1] = Left; + + QueueMsg(conn, Mess, Left + 2); + Flush(conn); + + conn->YAPPLen += Left; + conn->MailBufferSize -= Left; + } +} + +char * AddUser(char * Call, char * password, BOOL BBSFlag) +{ + struct UserInfo * USER; + + strlop(Call, '-'); + + if (strlen(Call) > 6) + Call[6] = 0; + + _strupr(Call); + + if (Call[0] == 0 || LookupCall(Call)) + { + return("User already exists\r\n"); + } + + USER = AllocateUserRecord(Call); + USER->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (strlen(password) > 12) + password[12] = 0; + + strcpy(USER->pass, password); + + if (BBSFlag) + { + if(SetupNewBBS(USER)) + USER->flags |= F_BBS; + else + printf("Cannot set user to be a BBS - you already have 160 BBS's defined\r\n"); + } + + SaveUserDatabase(); + UpdateWPWithUserInfo(USER); + + return("User added\r\n"); +} + +// Server Support Code + +// For the moment only internal REQDIR and REQFIL. + +// May add WPSERV and user implemented servers +/* +F6FBB BBS > + SP REQDIR @ F6ABJ.FRA.EU + Title of message : + YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + F6FBB BBS > + SP REQFIL @ F6ABJ.FRA.EU + Title of message : + DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + Note Text not used. + +*/ + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To); + +BOOL ProcessReqDir(struct MsgInfo * Msg) +{ + char * Buffer; + int Len = 0; + char * ptr; + + // Parse title - gives directory and return address + + // YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char Pattern[64]; + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + +#ifdef WIN32 + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + +#else + + #include + + struct dirent **namelist; + int n, i; + struct stat STAT; + int res; + char FN[256]; + +#endif + + strcpy(Pattern, Msg->title); + + ptr = strchr(Pattern, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(Pattern, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize); + +#ifdef WIN32 + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\"); + strcat(szDir, Pattern); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + Len = sprintf(Buffer, "No Files\r"); + } + else + { + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + Len += sprintf(&Buffer[Len], "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + } + +#else + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + Len += sprintf(&Buffer[Len], "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + +#endif + + // Build Message + + SendServerReply("REQDIR Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +/* + ' Augment Message ID with the Message Pickup Station we're directing this message to. + ' + Dim strAugmentedMessageID As String + If GetMidRMS(MessageId) <> "" Then + ' The MPS RMS is already set on the message ID + strAugmentedMessageID = MessageId + strMPS = GetMidRMS(MessageId) + ' "@R" at the end of the MID means route message only via radio + If GetMidForwarding(MessageId) = "" And (blnRadioOnly Or UploadThroughInternet()) Then + strAugmentedMessageID &= "@" & strHFOnlyFlag + End If + ElseIf strMPS <> "" Then + ' Add MPS to the message ID + strAugmentedMessageID = MessageId & "@" & strMPS + ' "@R" at the end of the MID means route message only via radio + If blnRadioOnly Or UploadThroughInternet() Then + strAugmentedMessageID &= "@" & strHFOnlyFlag + End If + Else + strAugmentedMessageID = MessageId + End If + +*/ + +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + Buffer[len] = 0; + + if (conn->Flags & GETTINGSYNCMESSAGE) + { + // Data + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + if (conn->TempMsg->length >= conn->SyncCompressedLen) + { + // Complete - decompress it + + conn->BBSFlags |= FBBCompressed; + Decode(conn, 1); + + conn->Flags &= !GETTINGSYNCMESSAGE; + + BBSputs(conn, "OK\r"); + return; + } + return; + } + + if (conn->Flags & PROPOSINGSYNCMSG) + { + // Waiting for response to TR AddMessage + + if (strcmp(Buffer, "OK\r") == 0) + { + char Msg[256]; + int n; + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Send the message, it has already been built + + conn->Flags &= !PROPOSINGSYNCMSG; + conn->Flags |= SENDINGSYNCMSG; + + n = sprintf_s(Msg, sizeof(Msg), "Sending SYNC message %s", conn->FwdMsg->bid); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + QueueMsg(conn, conn->SyncMessage, conn->SyncCompressedLen); + return; + } + + if (strcmp(Buffer, "NO\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Message Rejected - ? duplicate + + if (conn->FwdMsg) + { + // Zap the entry + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + } + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->Flags &= !PROPOSINGSYNCMSG; + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (conn->Flags & SENDINGSYNCMSG) + { + if (strcmp(Buffer, "OK\r") == 0) + { + // Message Sent + + conn->Flags &= !SENDINGSYNCMSG; + free(conn->SyncMessage); + + if (conn->FwdMsg) + { + char Msg[256]; + int n; + + n = sprintf_s(Msg, sizeof(Msg), "SYNC message %s Sent", conn->FwdMsg->bid); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + + // drop through to send any more + } + else + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + conn->Flags &= !SENDINGSYNCMSG; + free(conn->SyncMessage); + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + + return; + } + } + + if (strcmp(Buffer, "OK\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Send Message(?s) to RMS Relay SYNC + +/* +OK +>TR AddMessage_V5JLSGH591JR 786 1219 522 True +BYE*/ + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg = conn->FwdMsg; + char Buffer[128]; + char * Message; + + Message = FormatSYNCMessage(conn, Msg); + + // Need to compress it + + conn->SyncMessage = malloc(conn->SyncXMLLen + conn->SyncMsgLen + 4096); + + conn->SyncCompressedLen = Encode(Message, conn->SyncMessage, conn->SyncXMLLen + conn->SyncMsgLen, 0, 1); + + sprintf(Buffer, "TR AddMessage_%s %d %d %d True\r", // The True on end indicates compressed + Msg->bid, conn->SyncCompressedLen, conn->SyncXMLLen, conn->SyncMsgLen); + + free(Message); + + conn->Flags |= PROPOSINGSYNCMSG; + + BBSputs(conn, Buffer); + return; + } + + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "TR ", 2) == 0) + { + // Messages have TR_COMMAND_BID Compressed Len XML Len Bosy Len + + char * Command; + char * BIDptr; + + BIDRec * BID; + char *ptr1, *ptr2, *context; + + // TR AddMessage_1145_G8BPQ 727 1202 440 True + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + Command = strtok_s(&Buffer[3], "_", &context); + BIDptr = strtok_s(NULL, " ", &context); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncCompressedLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncXMLLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncMsgLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + + // If addmessage need to check bid doesn't exist + + if (strcmp(Command, "AddMessage") == 0) + { + strlop(BIDptr, '@'); // sometimes has @CALL@R + if (strlen(BIDptr) > 12) + BIDptr[12] = 0; + + BID = LookupBID(BIDptr); + + if (BID) + { + BBSputs(conn, "Rejected - Duplicate BID\r"); + return; + } + } + + conn->TempMsg = zalloc(sizeof(struct MsgInfo)); + + conn->Flags |= GETTINGSYNCMESSAGE; + + BBSputs(conn, "OK\r"); + return; + } + + if (memcmp(Buffer, "BYE\r", 4) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "BBS\r", 4) == 0) + { + // Out of Sync + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags &= ~SYNCMODE; + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + WriteLogLine(conn, '<', "Unexpected SYNC Message", 23, LOG_BBS); + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; +} +BOOL ProcessReqFile(struct MsgInfo * Msg) +{ + char FN[128]; + char * Buffer; + int Len = 0; + char * ptr; + struct stat STAT; + char MsgFile[MAX_PATH]; + FILE * hFile; + int FileSize; + char * MsgBytes; + + // Parse title - gives file and return address + + // DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + + strcpy(FN, Msg->title); + + ptr = strchr(FN, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(FN, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize + 1); // Allow terminating Null + + // Build Message + + if (FN == NULL) + { + Len = sprintf(Buffer, "Missing Filename\r"); + } + else if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Len = sprintf(Buffer,"Invalid filename %s\r", FN); + } + else + { + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, FN); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", FN); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + if (FileSize > MaxTXSize) + FileSize = MaxTXSize; // Truncate to max size + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + Len = sprintf(Buffer, "%s", MsgBytes); + free(MsgBytes); + } + } + else + Len = sprintf(Buffer, "File %s not found\r", FN); + } + + SendServerReply("REQFIL Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +BOOL CheckforMessagetoServer(struct MsgInfo * Msg) +{ + if (_stricmp(Msg->to, "REQDIR") == 0) + return ProcessReqDir(Msg); + + if (_stricmp(Msg->to, "REQFIL") == 0) + return ProcessReqFile(Msg); + + return FALSE; +} + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + char * Via; + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, BBSName); + Via = strlop(To, '@'); + + if (Via) + strcpy(Msg->via, Via); + + strcpy(Msg->to, To); + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +void SendRequestSync(CIRCUIT * conn) +{ + // Only need XML Header + + char * Buffer = malloc(4096); + int Len = 0; + + struct tm *tm; + char Date[32]; + char MsgTime[32]; + time_t Time = time(NULL); + + char * Encoded; + + tm = gmtime(&Time); + + sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + Len += sprintf(&Buffer[Len], "\r\n"); + + Len += sprintf(&Buffer[Len], "\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " request_sync\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Date); + Len += sprintf(&Buffer[Len], " %s\r\n", BBSName); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " BBSName\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", conn->SyncHost); + Len += sprintf(&Buffer[Len], " %d\r\n", conn->SyncPort); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); + +/* + + + + request_sync + 20230205100652 + GI8BPQ + + + GI8BPQ + + 127.0.0.1 + 8780 + + + +*/ + + // Need to compress it + + conn->SyncXMLLen = Len; + conn->SyncMsgLen = 0; + + conn->SyncMessage = malloc(conn->SyncXMLLen + 4096); + + conn->SyncCompressedLen = Encode(Buffer, conn->SyncMessage, conn->SyncXMLLen, 0, 1); + + sprintf(Buffer, "TR RequestSync_%s_%d %d %d 0 True\r", // The True on end indicates compressed + 50, conn->SyncCompressedLen, conn->SyncXMLLen); + + free(Buffer); + + conn->Flags |= REQUESTINGSYNC; + + BBSputs(conn, Buffer); + return; +} + + +void ProcessSyncXML(CIRCUIT * conn, char * XML) +{ + // Process XML from RMS Relay Sync + + // All seem to start + + // + // + // + // + + char * Type = strstr(XML, ""); + + if (Type == NULL) + return; + + Type += strlen(""); + + if (memcmp(Type, "rms_location", 12) == 0) + { + return; + } + + + if (memcmp(Type, "request_sync", 12) == 0) + { + char * Call; + struct UserInfo * BBSREC; + + // This isn't requesting a poll, it is asking to be added as a sync partner + + Call = strstr(Type, ""); + + if (Call == NULL) + return; + + Call += 10; + strlop(Call, '<'); + BBSREC = FindBBS(Call); + + if (BBSREC == NULL) + return; + + if (BBSREC->ForwardingInfo->Forwarding == 0) + StartForwarding(BBSREC->BBSNumber, NULL); + + return; + } + + if (memcmp(Type, "remove_message", 14) == 0) + { + char * MID = strstr(Type, ""); + struct MsgInfo * Msg; + + if (MID == NULL) + return; + + MID += 11; + strlop(MID, '<'); + + strlop(MID, '@'); // sometimes has @CALL@R + if (strlen(MID) > 12) + MID[12] = 0; + + Msg = FindMessageByBID(MID); + + if (Msg == NULL) + return; + + Logprintf(LOG_BBS, conn, '|', "Killing Msg %d %s", Msg->number, Msg->bid); + + FlagAsKilled(Msg, TRUE); + return; + } + + if (memcmp(Type, "delivered", 9) == 0) + { + char * MID = strstr(Type, ""); + struct MsgInfo * Msg; + + if (MID == NULL) + return; + + MID += 11; + strlop(MID, '<'); + + strlop(MID, '@'); // sometimes has @CALL@R + if (strlen(MID) > 12) + MID[12] = 0; + + Msg = FindMessageByBID(MID); + + if (Msg == NULL) + return; + + Logprintf(LOG_BBS, conn, '|', "Message Msg %d %s Delivered", Msg->number, Msg->bid); + return; + } + + Debugprintf(Type); + return; + +/* + + + + request_sync + 20230205100652 + GI8BPQ + + + GI8BPQ + + 127.0.0.1 + 8780 + + + +} + + + + delivered + 20230205093113 + G8BPQ + + + 10845_GM8BPB + G8BPQ + G8BPQ + 3 + + + + Public Enum MessageDeliveryMethod + ' + ' Method used to deliver a message. None if the message hasn't been delivered. + ' + Unspecified = -1 + None = 0 + Telnet = 1 + CMS = 2 + Radio = 3 + Email = 4 +End Enum +*/ +} + +int ReformatSyncMessage(CIRCUIT * conn) +{ + // Message has been decompressed - reformat to look like a WLE message + + char * MsgBit; + char *ptr1, *ptr2; + int linelen; + char FullFrom[80]; + char FullTo[80]; + char BID[80]; + time_t Date; + char Mon[80]; + char Subject[80]; + int i = 0; + char * Boundary; + char * Input; + char * via = NULL; + char * NewMsg = conn->MailBuffer; + char * SaveMsg = NewMsg; + char DateString[80]; + struct tm * tm; + char Type[16] = "Private"; + char * part[100] = {""}; + char * partname[100]; + int partLen[100]; + char xml[4096]; + + // Message has an XML header then the message + + // The XML may have control info, so examine it. + + /* + Date: Mon, 25 Oct 2021 10:22:00 -0000 + From: GM8BPQ + Subject: Test + To: 2E1BGT + Message-ID: ALYJQJRXVQAO + X-Source: GM8BPQ + X-Relay: G8BPQ + MIME-Version: 1.0 + MIME-Version: 1.0 + Content-Type: multipart/mixed; boundary="boundaryBSoxlw==" + + --boundaryBSoxlw== + Content-Type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Hello Hello + + --boundaryBSoxlw==-- + */ + + // I think the best way is to reformat as if from Winlink Express, then pass + //through the normal B2 code. + +// WriteLogLine(conn, '<', conn->MailBuffer, conn->TempMsg->length, LOG_BBS); + + // display the message for testing + + conn->MailBuffer[conn->TempMsg->length] = 0; + +// OutputDebugString(conn->MailBuffer); + memcpy(xml, conn->MailBuffer, conn->SyncXMLLen); + xml[conn->SyncXMLLen] = 0; + + if (conn->SyncMsgLen == 0) + { + // No message, Just xml. Looks like a status report + + ProcessSyncXML(conn, xml); + return 0; + } + + MsgBit = &conn->MailBuffer[conn->SyncXMLLen]; + conn->TempMsg->length -= conn->SyncXMLLen; + + ptr1 = MsgBit; + +Loop: + + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0) + { + memcpy(FullFrom, &ptr1[6], linelen - 6); + FullFrom[linelen - 6] = 0; + } + + if (_memicmp(ptr1, "To:", 3) == 0) + { + memcpy(FullTo, &ptr1[4], linelen - 4); + FullTo[linelen - 4] = 0; + } + + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + memcpy(Subject, &ptr1[9], linelen - 9); + Subject[linelen - 9] = 0; + } + + else if (_memicmp(ptr1, "Message-ID", 10) == 0) + { + memcpy(BID, &ptr1[12], linelen - 12); + BID[linelen - 12] = 0; + } + + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Mon, 25 Oct 2021 10:22:00 -0000 + + sscanf(&ptr1[11], "%02d %s %04d %02d:%02d:%02d", + &rtime.tm_mday, &Mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + for (i = 0; i < 12; i++) + { + if (strcmp(Mon, month[i]) == 0) + break; + } + + rtime.tm_mon = i; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + // Unpack Body - seems to be multipart even if only one + + // Can't we just send the whole body through ?? + // No, Attachment format is different + + // Mbo: GM8BPQ + // Body: 17 + // File: 1471 leadercoeffs.txt + + Input = MsgBit; + Boundary = initMultipartUnpack(&Input); + + i = 0; + + if (Boundary) + { + // input should be start of part + + // Find End of part - ie -- Boundary + CRLF or -- + + char * ptr, * saveptr; + char * Msgptr; + size_t BLen = strlen(Boundary); + size_t Partlen; + + saveptr = Msgptr = ptr = Input; + + while(ptr) // Just in case we run off end + { + if (*ptr == '-' && *(ptr+1) == '-') + { + if (memcmp(&ptr[2], Boundary, BLen) == 0) + { + // Found Boundary + + char * p1, *p2, *ptr3, *ptr4; + int llen; + int Base64 = 0; + int QuotedP = 0; + char * BoundaryStart = ptr; + + Partlen = ptr - Msgptr; + + ptr += (BLen + 2); // End of Boundary + + if (*ptr == '-') // Terminating Boundary + Input = NULL; + else + Input = ptr + 2; + + // Will check for quoted printable + + p1 = Msgptr; +Loop2: + p2 = strchr(p1, '\r'); + llen = (int)(p2 - p1); + + if (llen) + { + + if (_memicmp(p1, "Content-Transfer-Encoding:", 26) == 0) + { + if (_memicmp(&p1[27], "base64", 6) == 0) + Base64 = TRUE; + else if (_memicmp(&p1[27], "quoted", 6) == 0) + QuotedP = TRUE; + } + else if (_memicmp(p1, "Content-Disposition: ", 21) == 0) + { + ptr3 = strstr(&p1[21], "name"); + + if (ptr3) + { + ptr3 += 5; + if (*ptr3 == '"') ptr3++; + ptr4 = strchr(ptr3, '"'); + if (ptr4) *ptr4 = 0; + + partname[i] = ptr3; + } + } + + if (llen) // Not Null line + { + p1 = p2 + 2; // Skip crlf + goto Loop2; + } + } + + part[i] = strstr(p2, "\r\n"); // Over separator + + if (part[i]) + { + part[i] += 2; + partLen[i] = BoundaryStart - part[i] - 2; + if (QuotedP) + partLen[i] = decode_quoted_printable(part[i], partLen[i]); + else if (Base64) + { + int Len = partLen[i], NewLen; + char * ptr = part[i]; + char * ptr2 = part[i]; + + // WLE sends base64 with embedded crlf, so remove them + + while (Len-- > 0) + { + if ((*ptr) != 10 && (*ptr) != 13) + *(ptr2++) = *(ptr++); + else + ptr ++; + } + + Len = ptr2 - part[i]; + ptr = part[i]; + ptr2 = part[i]; + + while (Len > 0) + { + decodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - part[i]); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + partLen[i] = NewLen; + } + } + Msgptr = ptr = Input; + i++; + continue; } + + // See if more parts + } + ptr++; + } + ptr++; + } + + + // Build the message + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + NewMsg += sprintf(NewMsg, + "MID: %s\r\n" + "Date: %s\r\n" + "Type: %s\r\n" + "From: %s\r\n", + BID, DateString, Type, FullFrom); + +// if (ToCalls) +// { +// int i; + +// for (i = 0; i < Calls; i++) +// NewMsg += sprintf(NewMsg, "To: %s\r\n", ToCalls[i]); + +// } +// else + { + NewMsg += sprintf(NewMsg, "To: %s\r\n", + FullTo); + } +// if (WebMail->CC && WebMail->CC[0]) +// NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC); + + NewMsg += sprintf(NewMsg, + "Subject: %s\r\n" + "Mbo: %s\r\n", + Subject, BBSName); + + // Write the Body: line and any File Lines + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", partLen[0]); + + i = 1; + + while (part[i]) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", + partLen[i], partname[i]); + + i++; + } + + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header + + // Now add parts + + i = 0; + + while (part[i]) + { + memmove(NewMsg, part[i], partLen[i]); + NewMsg += partLen[i]; + i++; + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line between attachments + } + + conn->TempMsg->length = NewMsg - SaveMsg; + conn->TempMsg->datereceived = conn->TempMsg->datechanged = time(NULL); + conn->TempMsg->datecreated = Date; + strcpy(conn->TempMsg->bid, BID); + + if (strlen(Subject) > 60) + Subject[60] = 0; + + strcpy(conn->TempMsg->title, Subject); + + return TRUE; +} + +char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg) +{ + // First an XML Header + + char * Buffer = malloc(4096 + Msg->length); + int Len = 0; + + struct tm *tm; + char Date[32]; + char MsgTime[32]; + char Separator[33]=""; + time_t Time = time(NULL); + char * MailBuffer; + int BodyLen; + char * Encoded; + + // Get the message - may need length in header + + MailBuffer = ReadMessageFile(Msg->number); + + BodyLen = Msg->length; + + // Remove any B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MailBuffer, "Body:"); + if (ptr) + { + BodyLen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + { + memcpy(MailBuffer, ptr + 4, BodyLen); + MailBuffer[BodyLen] = 0; + } + } + + // encode body as quoted printable; + + Encoded = malloc(Msg->length * 3); + + BodyLen = encode_quoted_printable(MailBuffer, Encoded, BodyLen); + + // Create multipart Boundary + + CreateOneTimePassword(&Separator[0], "Key", 0); + CreateOneTimePassword(&Separator[16], "Key", 1); + + + tm = gmtime(&Time); + + sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + Len += sprintf(&Buffer[Len], "\r\n"); + + Len += sprintf(&Buffer[Len], "\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " add_message\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Date); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); + Len += sprintf(&Buffer[Len], " \r\n", MsgTime); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " 2\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", (Msg->B2Flags & Attachments) ? "true" : "false"); + Len += sprintf(&Buffer[Len], " %d\r\n", BodyLen); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->title); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); + Len += sprintf(&Buffer[Len], " 450443\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->to); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " 0\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " True\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); + +// Debugprintf(Buffer); + + conn->SyncXMLLen = Len; + + Len += sprintf(&Buffer[Len], "Date: Sat, 04 Feb 2023 11:19:00 +0000\r\n"); + Len += sprintf(&Buffer[Len], "From: %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], "Subject: %s\r\n", Msg->title); + Len += sprintf(&Buffer[Len], "To: %s\r\n", Msg->to); + Len += sprintf(&Buffer[Len], "Message-ID: %s\r\n", Msg->bid); +// Len += sprintf(&Buffer[Len], "X-Source: G8BPQ\r\n"); +// Len += sprintf(&Buffer[Len], "X-Location: 52.979167N, 1.125000W (GRID SQUARE)\r\n"); +// Len += sprintf(&Buffer[Len], "X-RMS-Originator: G8BPQ\r\n"); +// Len += sprintf(&Buffer[Len], "X-RMS-Path: G8BPQ@2023-02-04-11:19:29\r\n"); + Len += sprintf(&Buffer[Len], "X-Relay: %s\r\n", BBSName); + + Len += sprintf(&Buffer[Len], "MIME-Version: 1.0\r\n"); + Len += sprintf(&Buffer[Len], "Content-Type: multipart/mixed; boundary=\"%s\"\r\n", Separator); + + Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before separator + Len += sprintf(&Buffer[Len], "--%s\r\n", Separator); + Len += sprintf(&Buffer[Len], "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n"); + Len += sprintf(&Buffer[Len], "Content-Transfer-Encoding: quoted-printable\r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before body + + Len += sprintf(&Buffer[Len], "%s\r\n", Encoded); + Len += sprintf(&Buffer[Len], "--%s--\r\n", Separator); + + conn->SyncMsgLen = Len - conn->SyncXMLLen; + + free(Encoded); + free(MailBuffer); + + return Buffer; +} + +int encode_quoted_printable(char *s, char * out, int Len) +{ + int n = 0; + char * start = out; + + while(Len--) + { + if (n >= 73 && *s != 10 && *s != 13) + {strcpy(out, "=\r\n"); n = 0; out +=3;} + if (*s == 10 || *s == 13) {putchar(*s); n = 0;} + else if (*s<32 || *s==61 || *s>126) + out += sprintf(out, "=%02x", (unsigned char)*s); + else if (*s != 32 || (*(s+1) != 10 && *(s+1) != 13)) + {*(out++) = *s; n++;} + else n += printf("=20"); + + s++; + } + *out = 0; + + return out - start; +} + +int decode_quoted_printable(char *ptr, int len) +{ + // overwrite input with decoded version + + char * ptr2 = ptr; + char * End = ptr + len; + char * Start = ptr; + + while (ptr < End) + { + if ((*ptr) == '=') + { + char c = *(++ptr); + char d; + + c = c - 48; + if (c < 0) + { + // = CRLF as a soft break + + ptr += 2; + continue; + } + if (c > 9) c -= 7; + d = *(++ptr); + d = d - 48; + if (d > 9) d -= 7; + + *(ptr2) = c << 4 | d; + ptr2++; + ptr++; + } + else + *ptr2++ = *ptr++; + } + return ptr2 - Start; +} diff --git a/BPQMail.c b/BPQMail.c index 82e58bc..ab8c86c 100644 --- a/BPQMail.c +++ b/BPQMail.c @@ -1110,10 +1110,12 @@ // Fix Webmail auto-refresh when page exceeds 64K bytes (54) // Fix Webmail send when using both headers/footers and attachmonts (55) // Fix R: line corruption on some 64 bit builds -// Dont drop empty lines inm TEXTFORWARDING (61) +// Dont drop empty lines in TEXTFORWARDING (61) // Dont wait for body prompt for TEXTFORWARDING for SID [PMS-3.2-C$] (62) // Add forwarding mode SETCALLTOSENDER for PMS Systems that don't accept < in SP (63) // QtTerm Monitoring fixed for 63 port version of BPQ (69) +// Fix to UI system to support up to 63 ports (79) +// Fix recently introduced crash when "Don't allow new users" is set (81) #include "bpqmail.h" @@ -1711,7 +1713,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, } /* ---------- TAJ --PG Server------*/ - if ( user->Temp->RUNPGPARAMS ) { + if (user->Temp && user->Temp->RUNPGPARAMS ) { printf("Also freeing RUNPGARGS\n"); free(user->Temp->RUNPGPARAMS); diff --git a/BPQMail.c.bak b/BPQMail.c.bak index ff79254..a4eecaf 100644 --- a/BPQMail.c.bak +++ b/BPQMail.c.bak @@ -1,3651 +1,3651 @@ -// Mail and Chat Server for BPQ32 Packet Switch -// -// - -// Version 1.0.0.17re - -// Split Messasge, User and BBS Editing from Main Config. -// Add word wrap to Console input and output -// Flash Console on chat user connect -// Fix processing Name response in chat mode -// Fix processing of *RTL from station not defined as a Chat Node -// Fix overlength lines ln List responses -// Housekeeping expires BIDs -// Killing a message removes it from the forwarding counts - -// Version 1.0.0.18 - -// Save User Database when name is entered or updated so it is not lost on a crash -// Fix Protocol Error in Compressed Forwarding when switching direction -// Add Housekeeping results dialog. - -// Version 1.0.0.19 - -// Allow PACLEN in forward scripts. -// Store and forward messages with CRLF as line ends -// Send Disconnect after FQ ( for LinFBB) -// "Last Listed" is saved if MailChat is closed without closing Console -// Maximum acceptable message length can be specified (in Forwarding Config) - -// Version 1.0.0.20 - -// Fix error in saving forwarding config (introduced in .19) -// Limit size of FBB forwarding block. -// Clear old connection (instead of new) if duplicate connect on Chat Node-Node link -// Send FA for Compressed Mail (was sending FB for both Compressed and Uncompressed) - -// Version 1.0.0.21 - -// Fix Connect Script Processing (wasn't waiting for CONNECTED from last step) -// Implement Defer -// Fix MBL-style forwarding -// Fix Add User (Params were not saved) -// Add SC (Send Copy) Command -// Accept call@bbs as well as call @ bbs - -// Version 1.0.0.22 - -// Implement RB RP LN LR LF LN L$ Commands. -// Implement QTH and ZIP Commands. -// Entering an empty Title cancels the message. -// Uses HomeBBS field to set @ field for local users. -// Creates basic WP Database. -// Uses WP to lookup @ field for non-local calls. -// Console "Actions" Menu renamed "Options". -// Excluded flag is actioned. -// Asks user to set HomeBBS if not already set. -// Fix "Shrinking Message" problem, where message got shorter each time it was read Initroduced in .19). -// Flash Server window when anyone connects to chat (If Console Option "Flash on Chat User Connect" set). - -// Version 1.0.0.23 - -// Fix R: line scan bug - -// Version 1.0.0.24 - -// Fix closing console window on 'B'. -// Fix Message Creation time. -// Enable Delete function in WP edit dialog - -// Version 1.0.0.25 - -// Implement K< and K> commands -// Experimental support for B1 and B2 forwarding -// Experimental UI System -// Fix extracting QTH from WP updates - -// Version 1.0.0.26 - -// Add YN etc responses for FBB B1/B2 - -// Version 1.0.0.27 - -// Fix crash if NULL received as start of a packet. -// Add Save WP command -// Make B2 flag BBS-specific. -// Implement B2 Send - -// Version 1.0.0.28 - -// Fix parsing of smtp to addresses - eg smtp:john.wiseman@cantab.net -// Flag messages as Held if smtp server rejects from or to addresses -// Fix Kill to (K> Call) -// Edit Message dialog shows latest first -// Add chat debug window to try to track down occasional chat connection problems - -// Version 1.0.0.29 - -// Add loads of try/excspt - -// Version 1.0.0.30 - -// Writes Debug output to LOG_DEBUG_X and Monitor Window - -// Version 1.0.0.32 - -// Allow use of GoogleMail for ISP functions -// Accept SYSOP as alias for SYSOPCall - ie user can do SP SYSOP, and it will appear in sysop's LM, RM, etc -// Email Housekeeping Results to SYSOP - -// Version 1.0.0.33 - -// Housekeeping now runs at Maintenance Time. Maintenance Interval removed. -// Allow multiple numbers on R and K commands -// Fix L command with single number -// Log if Forward count is out of step with messages to forward. -// UI Processing improved and F< command implemented - -// Version 1.0.0.34 - -// Semaphore Chat Messages -// Display Semaphore Clashes -// More Program Error Traps -// Kill Messages more than BIDLifetime old - -// Version 1.0.0.35 - -// Test for Mike - Remove B1 check from Parse_SID - -// Version 1.0.0.36 - -// Fix calculation of Housekeeping Time. -// Set dialog box background explicitly. -// Remove tray entry for chat debug window. -// Add date to log file name. -// Add Actions Menu option to disable logging. -// Fix size of main window when it changes between versions. - -// Version 1.0.0.37 - -// Implement Paging. -// Fix L< command (was giving no messages). -// Implement LR LR mmm-nnn LR nnn- (and L nnn-) -// KM should no longer kill SYSOP bulls. -// ISP interfaces allows SMTP Auth to be configured -// SMTP Client would fail to send any more messages if a connection failed - -// Version 1.0.0.38 - -// Don't include killed messages in L commands (except LK!) -// Implement l@ -// Add forwarding timebands -// Allow resizing of main window. -// Add Ver command. - -// Version 1.0.1.1 - -// First Public Beta - -// Fix part line handling in Console -// Maintenance deletes old log files. -// Add option to delete files to the recycle bin. - -// Version 1.0.2.1 - -// Allow all Node SYSOP commands in connect scripts. -// Implement FBB B1 Protocol with Resume -// Make FBB Max Block size settable for each BBS. -// Add extra logging when Chat Sessions refused. -// Fix Crash on invalid housekeeping override. -// Add Hold Messages option. -// Trap CRT Errors -// Sort Actions/Start Forwarding List - -// Version 1.0.2.2 - -// Fill in gaps in BBS Number sequence -// Fix PE if ctext contains } -// Run Houskeeping at startup if previous Housekeeping was missed - -// Version 1.0.2.3 - -// Add configured nodes to /p listing - -// Version 1.0.2.4 - -// Fix RMS (it wanted B2 not B12) -// Send messages if available after rejecting all proposals -// Dont try to send msg back to originator. - -// Version 1.0.2.5 - -// Fix timeband processing when none specified. -// Improved Chat Help display. -// Add helpful responses to /n /q and /t - -// Version 1.0.2.6 - -// Kill Personal WP messages after processing -// Make sure a node doesnt try to "join" or "leave" a node as a user. -// More tracing to try to track down lost topic links. -// Add command recall to Console -// Show users in new topic when changing topic -// Add Send From Clipboard" Action - -// Version 1.0.2.7 - -// Hold messages from the future, or with invalid dates. -// Add KH (kill held) command. -// Send Message to SYSOP when a new user connects. - -// Version 1.0.2.8 - -// Don't reject personal message on Dup BID unless we already have an unforwarded copy. -// Hold Looping messages. -// Warn SYSOP of held messages. - -// Version 1.0.2.9 - -// Close connecton on receipt of *** DONE (MBL style forwarding). -// Improved validation in link_drop (Chat Node) -// Change to welcome prompt and Msg Header for Outpost. -// Fix Connect Script processing for KA Nodes - -// Version 1.0.3.1 - -// Fix incorrect sending of NO - BID. -// Fix problems caused by a user being connected to more than one chat node. -// Show idle time on Chat /u display. -// Rewrite forwarding by HA. -// Add "Bad Words" Test. -// Add reason for holding to SYSOP "Message Held" Message. -// Make topics case-insensitive. -// Allow SR for smtp mail. -// Try to fix some user's "Add User" problem. - - -// Version 1.0.3.2 - -// Fix program error when prcessing - response in FBB forwarding. -// Fix code to flag messages as sent. - - -// Version 1.0.3.3 - -// Attempt to fix message loop on topic_change -// Fix loop if compressed size is greater than 32K when receiving with B1 protocol. -// Fix selection of B1 - -// Version 1.0.3.4 - -// Add "KISS ONLY" Flag to R: Lines (Needs Node Version 4.10.12 (4.10l) or above) -// Add Basic NNTP Interface -// Fix possible loop in lzhuf encode - -// Version 1.0.3.5 - -// Fix forwarding of Held Messages -// More attempts to fix Chat crashes. -// Limit join/leave problem with mismatched nodes. -// Add Chat Node Monitoring System. -// Change order of elements in nntp addresses (now to.at, was at.to) - -// Version 1.0.3.6 - -// Restart and Exit if too many errors -// Fix forwarding of killed messages. -// Fix Forwarding to PaKet. -// Fix problem if BBS signon contains words from the "Fail" list - -// Version 1.0.3.7 - -// re-fix loop if compressed size is greater than 32K - reintroduced in 1.0.3.4 -// Add last message to edit users -// Change Console and Monitor Buffer sizes -// Don't flag msg as 'Y' on read if it was Held or Killed - -// Version 1.0.3.8 - -// Don't connect if all messages for a BBS are held. -// Hold message if From or To are missing. -// Fix parsing of /n and /q commands -// fix possible loop on changing name or qth - -// Version 1.0.3.9 - -// More Chat fixes and monitoring -// Added additional console for chat - -// Version 1.0.3.10 - -// Fix for corruption of CIrcuit-Node chain. - -// Version 1.0.3.11 - -// Fix flow control for SMTP and NNTP - -// Version 1.0.3.12 - -// Fix crash in SendChatStatus if no Chat Links Defined. -// Disable Chat Mode if there is no ApplCall for ChatApplNum, -// Add Edit Message to Manage Messages Dialog -// NNTP needs authentication - - -// Version 1.0.3.13 - -// Fix Chat ApplCall warning when ChatAppl = 0 -// Add NNTP NEWGROUPS Command -// Fix MBL Forwarding (remove extra > prompt after SP) - -// Version 1.0.3.14 - -// Fix topic switch code. -// Send SYSOP messages on POP3 interface if User SYSOP flag is set. -// NNTP only needs Authentication for posting, not reading. - -// Version 1.0.3.15 - -// Fix reset of First to Forward after househeeping - -// Version 1.0.3.16 - -// Fix check of HA for terminating WW -// MBL Mode remove extra > prompts -// Fix program error if WP record has unexpected format -// Connect Script changes for WINMOR -// Fix typo in unconfigured node has connected message - -// Version 1.0.3.17 - -// Fix forwarding of Personals - -// Version 1.0.3.18 - -// Fix detection of misconfigured nodes to work with new nodes. -// Limit connection attempt rate when a chat node is unavailable. -// Fix Program Error on long input lines (> ~250 chars). - -// Version 1.0.3.19 - -// Fix Restart of B2 mode transfers. -// Fix error if other end offers B1 and you are configured for B2 only. - - -// Version 1.0.3.20 - -// Fix Paging in Chat Mode. -// Report Node Versions. - -// Version 1.0.3.21 - -// Check node is not already known when processing OK -// Add option to suppress emailing of housekeeping results - -// Version 1.0.3.22 - -// Correct Version processing when user connects via the network -// Add time controlled forwarding scripts - -// Version 1.0.3.23 - -// Changes to RMS forwarding - -// Version 1.0.3.24 - -// Fix RMS: from SMTP interface -// Accept RMS/ instead of RMS: for Thunderbird - -// Version 1.0.3.25 - -// Accept smtp: addresses from smtp client, and route to ISP gateway. -// Set FROM address of messages from RMS that are delivered to smtp client so a reply will go back via RMS. - -// Version 1.0.3.26 - -// Improve display of rms and smtp messages in message lists and message display. - -// Version 1.0.3.27 - -// Correct code that prevents mail being retured to originating BBS. -// Tidy stuck Nodes and Topics when all links close -// Fix B2 handling of @ to TO Address. - -// Version 1.0.3.28 - -// Ensure user Record for the BBS Call has BBS bit set. -// Don't send messages addressed @winlink.org if addressee is a local user with Poll RMS set. -// Add user configurable welcome messages. - -// Version 1.0.3.29 - -// Add AUTH feature to Rig Control - -// Version 1.0.3.30 - -// Process Paclink Header (;FW:) - -// Version 1.0.3.31 - -// Process Messages with attachments. -// Add inactivity timeout to Chat Console sessions. - -// Version 1.0.3.32 - -// Fix for Paclink > BBS Addresses - -// Version 1.0.3.33 - -// Fix multiple transfers per session for B2. -// Kill messages eent to paclink. -// Add option to forward messages on arrival. - -// Version 1.0.3.34 - -// Fix bbs addresses to winlink. -// Fix adding @winlink.org to imcoming paclink msgs - -// Version 1.0.3.35 - -// Fix bbs addresses to winlink. (Again) - -// Version 1.0.3.36 - -// Restart changes for RMS/paclink - -// Version 1.0.3.37 - -// Fix for RMS Express forwarding - -// Version 1.0.3.38 - -// Fixes for smtp and lower case packet addresses from Airmail -// Fix missing > afer NO - Bid in MBL mode - -// Version 1.0.3.39 - -// Use ;FW: for RMS polling. - -// Version 1.0.3.40 - -// Add ELSE Option to connect scripts. - -// Version 1.0.3.41 - -// Improved handling of Multiple Addresses -// Add user colours to chat. - -// Version 1.0.3.42 - -// Poll multiple SSID's for RMS -// Colour support for BPQTEerminal -// New /C chat command to toggle colour on or off. - -// Version 1.0.3.43 - -// Add SKIPPROMPT command to forward scripts - -// Version 1.0.4.1 - -// Non - Beta Release -// Fix possible crash/corruption with long B2 messages - -// Version 1.0.4.2 - -// Add @winlink.org to the B2 From addresss if it is just a callsign -// Route Flood Bulls on TO as well as @ - -// Version 1.0.4.3 - -// Handle Packet Addresses from RMS Express -// Fix for Housekeeping B$ messages - -// Version 1.0.4.4 - -// Remove B2 header and all but the Body part from messages forwared using MBL -// Fix handling of ;FW: from RMS Express - -// Version 1.0.4.5 - -// Disable Paging on forwarding sessions. -// Kill Msgs sent to RMS Exxpress -// Add Name to Chat *** Joined msg - -// Version 1.0.4.6 - -// Pass smtp:winlink.org messages from Airmail to local user check -// Only apply local user check to RMS: messages @winlink.org -// Check locally input smtp: messages for local winlink.org users -// Provide facility to allow only one connect on a port - -// Version 1.0.4.8 - -// Only reset last listed on L or LR commands. - -// Version 1.0.4.9 - -// Fix error in handling smtp: messages to winlink.org addresses from Airmail - -// Version 1.0.4.10 - -// Fix Badwords processing -// Add Connect Script PAUSE command - -// Version 1.0.4.11 - -// Suppress display and listing of held messages -// Add option to exclude SYSOP messages from LM, KM, etc -// Fix crash whan receiving messages with long lines via plain text forwarding - -// Version 1.0.4.12 Jul 2010 - -// Route P messages on AT -// Allow Applications above 8 - -// Version 1.0.4.13 Aug 2010 - -// Fix TidyString for addresses of form John Wiseman -// Add Try/Except around socket routines - -// Version 1.0.4.14 Aug 2010 - -// Trap "Error - TNC Not Ready" in forward script response -// Fix restart after program error -// Add INFO command -// Add SYSOP-configurable HELP Text. - -// Version 1.0.4.15 Aug 2010 - -// Semaphore Connect/Disconnect -// Semaphore RemoveTempBIDS - -// Version 1.0.4.16 Aug 2010 - -// Remove prompt after receiving unrecognised line in MBL mode. (for MSYS) - -// Version 1.0.4.17 Aug 2010 - -// Fix receiving multiple messages in FBB Uncompressed Mode -// Try to trap phantom chat node connections -// Add delay to close - - -// Version 1.0.4.18 Aug 2010 - -// Add "Send SYSTEM messages to SYSOP Call" Option -// set fwd bit on local winlink.org msgs if user is a BBS -// add winlink.org to from address of messages from WL2K that don't already have an @ - -// Version 1.0.4.19 Sept 2010 - -// Build a B2 From: address if possible, so RMS Express can reply to packet messages. -// Fix handling of addresses from WL2K with SSID's -// L@ now only matches up to length of input string. -// Remove "Type H for help" from login prompt. - -// Version 1.0.4.20 Sept 2010 - -// Process FBB 'E' response -// Handle FROM addresses with an @BBS -// Fix FROM addresses with @ on end. -// Extend delay before close after sending FQ on winmor/pactor sessions. - -// Version 1.0.4.21 Sept 2010 - -// Fix handling B2 From: with an HA -// Add "Expert User" welcome message. - -// Version 1.0.4.22 Sept 2010 - -// Version 1.0.4.23 Oct 2010 - -// Add Dup message supression -// Dont change B2 from if going to RMS - -// Version 1.0.4.24 Oct 2010 - -// Add "Save Registry Config" command -// Add forwarding on wildcarded TO for NTS -// Add option to force text mode forwarding -// Define new users as a temporaty BBS if SID received in reply to Name prompt -// Reduce delay before sending close after sending FQ on pactor sessions -// Fix processing of MIME boundary from GMail - -// Send /ex instead of ctrl/z for text mode forwarding -// Send [WL2K-BPQ... SID if user flagged as RMS Express -// Fix Chat Map reporting when more than one AXIP port -// Add Message State D for NTS Messages -// Forward messages in priority order - T, P, B -// Add Reject and Hold Filters -// Fix holding messages to local RMS users when received as part of a multiple addressee message - -// Version 1.0.4.25 Nov 2010 - -// Renumbered for release -// Add option to save Registry Config during Housekeeping - -// Version 1.0.4.26 Nov 2010 - -// Fix F> loop when doing MBL forwarding between BPQ BBSes -// Allow multiple To: addresses, separated by ; -// Allow Houskeeping Lifetime Overrides to apply to Unsent Messages. -// Set Unforwarded Bulls to status '$' -// Accept MARS and USA as continent codes for MARS Packet Addresses -// Add option to send Non-delivery notifications. - -// Version 1.0.4.27 Dec 2010 - -// Add MSGTYPES fwd file option - -// Version 1.0.4.28 Dec 2010 - -// Renumbered to for release - -// Version 1.0.4.30 Dec 2010 - -// Fix rescan requeuing where bull was rejected by a BBS -// Fiz flagging bulls received by NNTP with $ if they need to be forwarded. -// Add Chat Keepalive option. -// Fix bug in non-delivery notification. - -// Version 1.0.4.32 Jan 2011 - -// Allow "Send from Clipboard" to send to rms: or smtp: -// Allow messages received via SMTP to be bulls (TO preceeded by bull/) or NTS (to nnnnn@NTSXX or nnnnn@NTSXX.NTS) -// Fix corruption of messages converted to B2 if body contains binary data -// Fix occasional program error when forwarding B2 messages -// Limit FBB protocol data blocks to 250 to try to fix restart problem. -// Add F2 to F5 to open windows. - -// Version 1.0.4.33 Jan 2011 - -// Fix holding old bulls with forwarding info. - -// Version 1.0.4.33 Jan 2011 - -// Prevent transfer restarting after a program error. -// Allow Housekeeping to kill held messages. - -// Version 1.0.4.35 Jan 2011 - -// Add Size limits for P and T messages to MSGTYPES command -// Fix Error in MBL processing when blank lines received (introduced in .33) -// Trap possible PE in Send_MON_Datagram -// Don't use paging on chat sessions - -// Version 1.0.4.36 Jan 2011 - -// Fix error after handling first FBB block. -// Add $X and $x welcome message options. - -// Version 1.0.4.37 Jan 2011 - -// Change L command not to list the last message if no new ones are available -// Add LC I I@ IH IZ commands -// Add option to send warning to sysop if forwarded P or T message has nowhere to go -// Fixes for Winpack Compressed Download -// Fix Houskeeping when "Apply Overrides to Unsent Bulls" is set. -// Add console copy/paste. -// Add "No Bulls" Option. -// Add "Mail For" Beacon. -// Tidied up Tab order in config dialogs to help text-to-speech programs. -// Limit MaxMsgno to 99000. - -// Version 1.0.4.38 Feb 2011 - -// Renumbered for release - -// Version 1.0.4.40 April 2011 - -// Add POLLRMS command - -// Changes for Vista/Win7 (registry key change) -// Workaround for changes to RMS Express -// Fix AUTH bug in SMTP server -// Add filter to Edit Messages dialog - -// Version 1.0.4.41 April 2011 - -// Extend B2 proposals to other BPQMail systems so Reject Filter will work. -// Add Edit User Command -// Use internal Registry Save routine instead of Regedit -// Fix Start Forward/All -// Allow Winpack Compressed Upload/Download if PMS flag set (as well as BBS flag) -// Add FWD SYSOP command -// Fix security on POLLRMS command -// Add AUTH command -// Leave selection in same place after Delete User -// Combine SMTP server messages to multiple WL2K addresses into one message to WL2k -// Add option to show name as well as call on Chat messages -// Fix program error if you try to define more than 80 BBS's - -// Version 1.0.4.45 October 2011 - -// Changes to program error reporting. -// BBS "Returh to Node" command added -// Move config to "Standard" location (BPQ Directory/BPQMailChat) . -// Fix crash if "Edit Message" clicked with no message selected. - -// Version 1.0.4.46 October 2011 - -// Fix BaseDir test when BaseDir ends with \ or / -// Fix long BaseDir values (>50 chars) - -// Version 1.4.47.1 January 2012 - -// Call CloseBPQ32 on exit -// Add option to flash window instead of sounding bell on Chat Connects -// Add ShowRMS SYSOP command -// Update WP with I records from R: lines -// Send WP Updates -// Fix Paclen on Pactor-like sessions -// Fix SID and Prompt when RMS Express User is set -// Try to stop loop in Program Error/Restarting code -// Trap "UNABLE TO CONNECT" response in connect script -// Add facility to print messages or save them to a text file - -// Version 1.4.48.1 January 2012 - -// Add Send Message (as well as Send from Clipboard) -// Fix Email From: Address when forwaring using B2 -// Send WP from BBSCALL not SYSOPCALL -// Send Chat Map reports via BPQ32.dll - - -// Version 1.4.49.1 February 2012 - - -// Fix Setting Paclink mode on SNOS connects -// Remove creation of debugging file for each message -// Add Message Export and Import functions -// All printing of more than one message at a time -// Add command to toggle "Expert" status - -// Version 1.4.50.1 February 2012 - -// Fix forwarding to RMS Express users -// Route messages received via B2 to an Internet email address to RMS -// Add Reverse Poll interval -// Add full FROM address to POP3 messages -// Include HOMEBBS command in Help - - -// Version 1.4.51.1 June 2012 - -// Allow bulls to be sent from RMS Express. -// Handle BASE64 and Quoted-printable encoding of single part messages -// Work round for RMS Express "All proposals rejected" Bug. - -// Version 1.4.52.1 August 2012 - -// Fix size limit on B2 To List when sending to multiple dests -// Fix initialisation of DIRMES.SYS control record -// Allow use of Tracker and UZ7HO ports for UI messages - -// Version 1.4.53.1 September 2012 - -// Fix crash if R: line with out a CR found. - -// Version 1.4.54.1 ?? 2012 - -// Add configurable prompts -// Fix KISS-Only Test -// Send EHLO instead of HELO when Authentication is needed on SMTP session -// Add option to use local tome for bbs forwarding config -// Allow comment lines (; or @) or single space in fwd scripts -// Fix loss of forwarding info if SAVE is clicked before selecting a call - -// Version 1.4.55.1 June 2013 - -// Add option to remove users that have not connected for a long time. -// Add l@ smtp: -// Fix From: sent to POP3 Client when meaages is from RMS -// Display Email From on Manage Messages - -// Version 1.4.56.1 July 2013 - -// Add timeout -// Verify prompts -// Add IDLETIME command - - - -// Version 1.4.57.1 - -// Change default IDLETIME -// Fix display of BBS's in Web "Manage Messages" -// Add separate househeeping lifetines for T messages -// Don't change flag on forwarded or delivered messages if they sre subsequently read -// Speed up processing, mainly to stop RMS Express timing out when connecting via Telnet -// Don't append winlink.org to RMS Express or Paclink addresses if RMS is not configured -// Fix receiving NTS messages via B2 -// Add option to send "Mail For", but not FBB Headers -// Fix corruption caused with Subject longer than 60 bytes reveived from Winlink systems -// Fix Endian bug in FBB Compression code - - -// Version 1.4.58.1 - -// Change control of appending winlink.org to RMS Express or Paclink addresses to a user flag -// Lookup HomeBBS and WP for calls without a via received from RMS Express or Paclink -// Treat call@bpq as request to look up address in Home BBS/WP for messages received from RMS Express or Paclink -// Collect stats by message type -// Fix Non-Delivery notifications to SMTP messages -// Add Message Type Stats to BBS Trafic Report -// Add "Batch forward to email" -// Add EXPORT command -// Allow more BBS records -// Allow lower case connect scripts -// Fix POP3 LIST command -// Fix MIME Multipart Alternate with first part Base64 or Quoted Printable encoding -// Fix duplicates of SP SYSOP@WW Messages -// Add command line option (tidymail) to delete redundant Mail files -// Add command line option (nohomebbs) to suppress HomeBBS prompt - -// 59 April 2014 - -// Add FLARQ Mail Mode -// Fix possible crash saving restart data -// Add script command ADDLF for connect scripts over Telnet -// Add recogniton of URONODE connected message -// Add option to stop Name prompt -// Add new RMS Express users with "RMS Express User" flag set -// Validate HTML Pages -// Add NTS swap file -// Add basic File list and read functions -// Fix Traffic report - -// 60 - -// Fix security hole in readfile - -// 61 August 2014 -// Set Messages to NTS:nnnnn@NTSXX to type 'T' and remove NTS -// Dont treat "Attempting downlink" as a failure -// Add option to read messages during a list -// Fix crash during message renumber on MAC -// Timeout response to SID to try to avoid hang on an incomplete connection. -// Save config in file instead of registry -// Fix Manage Messages "EXPORT" option and check filename on EXPORT command -// Fix reverse forward prompt in MBL mode. -// Fix From address in POP3 messages where path is @winlink.org -// Fix possible program error in T message procesing -// Add MaxAge param (for incoming Bulls) - - -//62 November 2014 -// Add ZIP and Permit Bulls flag to Manage Users -// Allow users to kill their own B and anyone to kill T messages -// Improve saving of "Last Listed" -// Fix LL when paging -// Send Date received in R: Line (should fix B2 message restarts) -// Fix occasional crash in terminal part line processing -// Add "SKIPCON" forwarding command to handle nodes that include "Connected" in their CTEXT -// Fix possible retry loop when message is deferred (FBB '=' response); -// Don't remove Attachments from received bulls. - -//63 Feb 2015 - -// Fix creating Bulls from RMS Express messages. -// Fix PE if message with no To: received. -// Fix setting "RMS Express User" flag on new connects from RMS Express -// Fix deleting 'T' messages downloaded by RMS Express -// Include MPS messages in count of messages to forward. -// Add new Welcome Message variable $F for messages to forward -// Fix setting Type in B2 header when usong NTS: or BULL: -// Remove trailing spaces from BID when Creating Message from Clipboard. -// Improved handling of FBB B1/B2 Restarts. - -//64 September 2015 - -// Fix Message Type in msgs from RMS Express to Internet -// Reopen Monitor window if open when program list closed -// Only apply NTS alias file to NTS Messages -// Fix failure to store some encrypted ISP passwords -// Allow EDITUSER to change "RMS Express User" flag -// Fix reporting of Config File errors -// Fix Finding MPS Messages (First to Forward was being used incorrectly) -// Add "Save Attachment" to Web Mgmt Interface -// Support Secure Signon on Forwarding sessions to CMS -// Save Forwarding config when BBS flag on user is cleared -// Pass internally generated SYSOP messages through routing process -// Add POP3 TOP command. -// Don't set 'T' messages to 'Y' when read. -// Add optional temporary connect script on "FWD NOW" command -// Add automatic import facility -// Accept RMS mail to BBS Call even if "Poll RMS" not set. - -// 65 November 2015 - -// Fix loading Housekeeping value for forwarded bulls. -// Fix re-using Fwd script override in timer driven forwarding. -// Add ampr.org handling -// Add "Dont forward" match on TO address for NTS -// Allow listing a combinatiom of state and type, such as LNT or LPF -// Fix handling ISP messages from gmail without a '+' -// Add basic WebMail support - -// 66 - -// Autoimport messages as Dummy Call, not SYSOP Call -// Add "My Messages" display option to WebMail -// Create .csv extract of User List during hourekeeping. -// Fix processing of NTS Alising of @ Addresses -// Don't reroute Delivered NTS Messages -// Add option to stop users killing T messages -// Add multicast Receive -// Fix initialising new message database format field -// Fix "Forward Messages to BBS Call" option. -// Add Filter WP Bulls option and allow multiple WP "TO" addresses -// Fix deleting P WP messages for other stations -// Fix saving blank lines in forwarding config -// Fix paging on L@ and l< -// Fix removing DELETE from IMPORT XXX DELETE and allow multiple IMPORT lines in script -// Run DeleteRedundantMessages before renumbering messages -// Connect script now tries ELSE lines if prompt not received from remote BBS -// Send connecting call instead of BBS Name when connecting to CMS server. -// Add BID filter to Manage Messages -// Fix handling of over long suject lines in IMPORT -// Allow comments before ELSE in connect script -// Add Copy and Clear to Multicast Window -// Fix possible duplicate messages with MBL forwarding -// Set "Permit EMail" on IMPORT dummy User. -// Fix repeated running of housekeeping if clock is stepped forward. -// Fix corruption of CMS Pass field by Web interface -// Kill B2 WP bulls if FilterWPBulls set -// Include Message Type in BPQ B2 proposal extensions - -// 6.0.14.1 July 2017 - -// Fix corruption of BBSNumber if RMS Ex User and BBS both checked -// Tread B messages without an AT as Flood. -// Make sure Message headers are always saved to disk when a message status changes -// Reject message instead of failing session if TO address too long in FBB forwarding -// Fix error when FBB restart data exactly fills a packet. -// Fix possible generation of msg number zero in send nondlivery notification -// Fix problem with Web "Manage Messages" when stray message number zero appears -// Fix Crash in AMPR forward when host missing from VIA -// Fix possible addition of an spurious password entry to the ;FW: line when connecting to CMS -// Fix test for Status "D" in forward check. -// Don't cancel AUTH on SMTP RSET -// Fix "nowhere to go" message on some messages sent to smtp addresses -// Add @ from Home BBS or WP is not spcified in "Send from Clipboard" - -// 6.0.15.1 Feb 2018 - -// Fix PE if Filename missing from FILE connect script command -// Suppress reporting errors after receiving FQ -// Fix problem caused by trailing spaces on callsign in WP database -// Support mixed case WINLINK Passwords - -// 6.0.16.1 March 2018 - -// Make sure messages sent to WL2K don;'t have @ on from: address -// If message to saildocs add R: line as an X header instead of to body -// Close session if more than 4 Invalid Commmad responses sent -// Report TOP in POP3 CAPA list. Allows POP3 to work with Windows Mail client - -// 6.0.17.1 November 2018 - -// Add source routing using ! eg sp g8bpq@winlink.org!gm8bpq to send via RMS on gm8bpq -// Accept an internet email address without rms: or smtp: -// Fix "Forward messages for BBS Call" when TO isn't BBS Call -// Accept NNTP commands in either case -// Add NNTP BODY command -// Timeout POP or SMTP TCP connections that are open too long -// Add YAPP support -// Fix connect script when Node CTEXT contains "} BBS " -// Fix handling null H Route -// Detect and correct duplicate BBS Numbers -// Fix problem if BBS requests FBB blocked forwarding without compression (ie SID of F without B) -// Fix crash if YAPP entered without filenmame and send BBS prompt after YAPP error messages -// Add support for Winlink HTML Forms to WebMail interface -// Update B2 header when using NTS alias file with B2 messages - -// 6.0.18.1 January 2019 - -// Ensure callsigns in WP database are upper case. -// Various fixes for Webmail -// Fix sending direct to ampr.org addresses -// Use SYSOP Call as default for Webmail if set -// Preparations for 64 bit version - - -// 6.0.19.1 September 2019 - -// Trap missing HTML reply Template or HTML files -// Fix case problems in HTML Templates -// Fix setting To call on reply to HTML messages -// More preparations for 64 bit including saving WP info as a text file. -// Set "RMS Express User" when a new user connects using PAT -// Increace maximum length on Forwarding Alias string in Web interface -// Expand multiaddress messages from Winlink Express if "Don't add @Winlink.org" set or no RMS BBS -// Fix program error if READ used without a filename -// Trap reject messages from Winlink CMS -// Fix "delete to recycle bin" on Linux -// Handle Radio Only Messages (-T or -R suffix on calling station) -// Fix program error on saving empty Alias list on Web Forwarding page -// Add REQDIR and REQFIL -// Experimental Blocked Uncompressed forwarding -// Security fix for YAPP -// Fix WebMail Cancel Send Message -// Fix processing Hold Message response from Winlink Express - -// 6.0.20.1 April 2020 - -// Improvments to YAPP -// Add Copy forwarding config -// Add Next and Previous buttons to Webmail message read screen -// Move HTML templates from HTMLPages to inline code. -// Fix Paclen on YAPP send -// Fix bug in handling "RMS Express User" -// Fix WINPACK compressed forwarding -// Add option to send P messages to more than one BBS -// Add "Default to Don't Add WINLINK.ORG" Config option -// Re-read Badwords.sys during Housekeeping -// Add BID Hold and Reject Filters -// On SMTP Send try HELO if EHLO rejected -// Allow SID response timeout to be configured per BBS -// Fix sending bulls with PAT -// Set "Forward Messages to BBS Call" when routing Bulls on TO -// Add option to send Mail For Message to APRS -// Fix WP update -// Fix Holding messages from Webmail Interface -// Add RMR command -// Add REROUTEMSGS BBS SYSOP command -// Disable null passwords and check Exclude flag in Webmail Signin -// Add basic Webmail logging - -// 6.0.21.1 December 2020 - -// Remove nulls from displayed messages. -// Fix Holding messages from SMTP and POP3 Interfaces -// Various fixes for handling messages to/from Internet email addresses -// Fix saving Email From field in Manage Messages -// Fix sending WL2K traffic reports via TriMode. -// Fix removing successive CR from Webmail Message display -// Fix Wildcarded @ forwarding -// Fix message type when receiving NTS Msgs form Airmail -// Fix address on SERVICE messages from Winlink -// Add multiple TO processing to Webmail non-template messages -// Don't backup config file if reading it fails -// Include Port and Freq on Connected log record -// Make sure welcome mesages don't end in > -// Allow flagging unread T messages as Delivered -// Replace \ with # in forward script so commands starting with # can be sent -// Fix forwarding NTS on TO field -// Fix possible crash in text mode forwarding -// Allow decimals of days in P message lifetimes and allow Houskeeping interval to be configured -// Add DOHOUSEKEEPING sysop command -// Add MARS continent code -// Try to trap 'zombie' BBS Sessions -// On Linux if "Delete to Recycle Bin" is set move deleted messages and logs to directory Deleted under current directory. -// Fix corruption of message length when reading R2 message via Read command -// Fix paging on List command and add new combinations of List options -// Fix NNTP list and LC command when bulls are killed - -// 6.0.22.1 August 2021 - -// Fix flagging messages with attachments as read. -// Fix possible corruption of WP database and subsequent crash on reloading. -// Fix format of Web Manage Messages display -// Include SETNEXTMESSAGENUMBER in SYSOP Help Message -// Fix occasional "Incoming Connect from SWITCH" -// Fix L> with numeric dests -// Improved diagnostic for MailTCP select() error. -// Clear "RMS Express User" if user is changed to a BBS -// Fix saving Window positions on exit -// Fix parsing ReplyTemplate name in Webmail -// Handle multiple addressees for WebMail Forms messages to packet stations -// Add option to allow only known users to connect -// Add basic callsign validation to From address -// Add option to forward a user's messages to Winlink -// Move User config to main config file. -// Update message status whne reading a Forms Webmail message -// Speed up killing multiple messages -// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command - -// 6.0.23.1 June 2022 - -// Fix crash when ; added to call in send commands -// Allow smtp/ override for messages from RMS Express to send via ISP gateway -// Send Internet email from RMS Express to ISP Gateway if enabled and RMS BBS not configured -// Recompiled for Web Interface changes in Node -// Add RMS Relay SYNC Mode (.17) -// Add Protocol changes for Relay RO forwarding -// Add SendWL2KPM command to connect script to allow users other than RMS to send ;FW: string to RMS Relay -// Fix B2 Header Date in Webmail message with sttachments. -// Fix bug when using YAPP with VARA (.27) -// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command -// Add mechsnism to send bbs log records to qttermtcp. (32) -// Add MFJ forwarding Mode (No @BBS on send) -// Fix handling CR/LF split over packet boundaries -// Add Header and Footers for Webmail Send (42) -// Fix Maintenance Interval in LinBPQ (53) -// Add RMS: to valid from addresses (.56) -// Fix Web management on Android deviced (.58) -// Disconnect immediately if "Invalid Command" "*** Protocol Error" or "Already Connected" received (.70) -// Check Badword and Reject filters before processing WP Messages - -// 6.0.24.1 ?? 2022 - -// Fix ' in Webmail subject (8) -// Change web buttons to white on black when pressed (10) -// Add auto-refresh option to Webmail index page (25) -// Fix displaying help and info files with crlf line endings on Linux (28) -// Improve validation of extended FC message (32) -// Improve WP check for SYSTEM as a callsign (33) -// Improvements to RMS Relay SYNC mode (47) -// Fix BID Hold and Reject filters -// Fix Webmail auto-refresh when page exceeds 64K bytes (54) -// Fix Webmail send when using both headers/footers and attachmonts (55) -// Fix R: line corruption on some 64 bit builds -// Dont drop empty lines inm TEXTFORWARDING (61) -// Dont wait for body prompt for TEXTFORWARDING for SID [PMS-3.2-C$] (62) -// Add forwarding mode SETCALLTOSENDER for PMS Systems that don't accept < in SP (63) -// QtTerm Monitoring fixed for 63 port version of BPQ (69) - - -#include "bpqmail.h" -#include "winstdint.h" -#define MAIL -#include "Versions.h" - -#include "GetVersion.h" - -#define MAX_LOADSTRING 100 - -typedef int (WINAPI FAR *FARPROCX)(); -typedef int (WINAPI FAR *FARPROCZ)(); - -FARPROCX pDllBPQTRACE; -FARPROCZ pGetLOC; -FARPROCX pRefreshWebMailIndex; -FARPROCX pRunEventProgram; - -BOOL WINE = FALSE; - -INT_PTR CALLBACK UserEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK MsgEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK FwdEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); - -VOID SetupNTSAliases(char * FN); - -HKEY REGTREE = HKEY_LOCAL_MACHINE; // Default -char * REGTREETEXT = "HKEY_LOCAL_MACHINE"; - -// Global Variables: -HINSTANCE hInst; // current instance -TCHAR szTitle[MAX_LOADSTRING]; // The title bar text -TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name - -extern int LastVer[4]; // In case we need to do somthing the first time a version is run - -UINT BPQMsg; - -HWND MainWnd; -HWND hWndSess; -RECT MainRect; -HMENU hActionMenu; -static HMENU hMenu; -HMENU hDisMenu; // Disconnect Menu Handle -HMENU hFWDMenu; // Forward Menu Handle - -int SessX, SessY, SessWidth; // Params for Session Window - -char szBuff[80]; - -#define MaxSockets 64 - -int _MYTIMEZONE = 0; - -ConnectionInfo Connections[MaxSockets+1]; - -//struct SEM AllocSemaphore = {0, 0}; -//struct SEM ConSemaphore = {0, 0}; -//struct SEM OutputSEM = {0, 0}; - -//struct UserInfo ** UserRecPtr=NULL; -//int NumberofUsers=0; - -//struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes - -//struct MsgInfo ** MsgHddrPtr=NULL; -//int NumberofMessages=0; - -//int FirstMessageIndextoForward=0; // Lowest Message wirh a forward bit set - limits search - -//BIDRec ** BIDRecPtr=NULL; -//int NumberofBIDs=0; - -extern BIDRec ** TempBIDRecPtr; -//int NumberofTempBIDs=0; - -//WPRec ** WPRecPtr=NULL; -//int NumberofWPrecs=0; - -extern char ** BadWords; -//int NumberofBadWords=0; -extern char * BadFile; - -//int LatestMsg = 0; -//struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg -//int HighestBBSNumber = 0; - -//int MaxMsgno = 60000; -//int BidLifetime = 60; -//int MaintInterval = 24; -//int MaintTime = 0; -//int UserLifetime = 0; - - -BOOL cfgMinToTray; - -BOOL DisconnectOnClose; - -extern char PasswordMsg[100]; - -char cfgHOSTPROMPT[100]; - -char cfgCTEXT[100]; - -char cfgLOCALECHO[100]; - -char AttemptsMsg[]; -char disMsg[]; - -char LoginMsg[]; - -char BlankCall[]; - - -ULONG BBSApplMask; -ULONG ChatApplMask; - -int BBSApplNum; - -//int StartStream=0; -int NumberofStreams; -int MaxStreams; - -extern char BBSSID[]; -extern char ChatSID[]; - -extern char NewUserPrompt[100]; - -extern char * WelcomeMsg; -extern char * NewWelcomeMsg; -extern char * ExpertWelcomeMsg; - -extern char * Prompt; -extern char * NewPrompt; -extern char * ExpertPrompt; - -extern BOOL DontNeedHomeBBS; - -char BBSName[100]; -char MailForText[100]; - -char SignoffMsg[100]; - -char AbortedMsg[100]; - -extern char UserDatabaseName[MAX_PATH]; -extern char UserDatabasePath[MAX_PATH]; - -extern char MsgDatabasePath[MAX_PATH]; -extern char MsgDatabaseName[MAX_PATH]; - -extern char BIDDatabasePath[MAX_PATH]; -extern char BIDDatabaseName[MAX_PATH]; - -extern char WPDatabasePath[MAX_PATH]; -extern char WPDatabaseName[MAX_PATH]; - -extern char BadWordsPath[MAX_PATH]; -extern char BadWordsName[MAX_PATH]; - -char NTSAliasesPath[MAX_PATH]; -extern char NTSAliasesName[MAX_PATH]; - -char BaseDir[MAX_PATH]; -char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% - -char MailDir[MAX_PATH]; - -char RlineVer[50]; - -extern BOOL KISSOnly; - -extern BOOL OpenMon; - -extern struct ALIAS ** NTSAliases; - -extern int EnableUI; -extern int RefuseBulls; -extern int SendSYStoSYSOPCall; -extern int SendBBStoSYSOPCall; -extern int DontHoldNewUsers; -extern int ForwardToMe; - -extern int MailForInterval; - -char zeros[NBMASK]; // For forward bitmask tests - -time_t MaintClock; // Time to run housekeeping - -struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List. - -// Filter Params - -char ** RejFrom; // Reject on FROM Call -char ** RejTo; // Reject on TO Call -char ** RejAt; // Reject on AT Call -char ** RejBID; // Reject on BID - -char ** HoldFrom; // Hold on FROM Call -char ** HoldTo; // Hold on TO Call -char ** HoldAt; // Hold on AT Call -char ** HoldBID; // Hold on BID - - -// Send WP Params - -BOOL SendWP; -char SendWPVIA[81]; -char SendWPTO[11]; -int SendWPType; - - -int ProgramErrors = 0; - -UCHAR BPQDirectory[260] = ""; - - -// Forward declarations of functions included in this code module: -ATOM MyRegisterClass(HINSTANCE hInstance); -ATOM RegisterMainWindowClass(HINSTANCE hInstance); -BOOL InitInstance(HINSTANCE, int); -LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); -INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); -INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); - -unsigned long _beginthread( void( *start_address )(VOID * DParam), - unsigned stack_size, VOID * DParam); - -VOID SendMailForThread(VOID * Param); -BOOL CreatePipeThread(); -int DeleteRedundantMessages(); -VOID BBSSlowTimer(); -VOID CopyConfigFile(char * ConfigName); -BOOL CreateMulticastConsole(); -char * CheckToAddress(CIRCUIT * conn, char * Addr); -BOOL CheckifPacket(char * Via); -int GetHTMLForms(); - -struct _EXCEPTION_POINTERS exinfox; - -CONTEXT ContextRecord; -EXCEPTION_RECORD ExceptionRecord; - -DWORD Stack[16]; - -BOOL Restarting = FALSE; - -Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg) -{ - unsigned int SPPtr; - unsigned int SPVal; - - memcpy(&ContextRecord, exinfo->ContextRecord, sizeof(ContextRecord)); - memcpy(&ExceptionRecord, exinfo->ExceptionRecord, sizeof(ExceptionRecord)); - - SPPtr = ContextRecord.Esp; - - Debugprintf("BPQMail *** Program Error %x at %x in %s", - ExceptionRecord.ExceptionCode, ExceptionRecord.ExceptionAddress, Msg); - - - __asm{ - - mov eax, SPPtr - mov SPVal,eax - lea edi,Stack - mov esi,eax - mov ecx,64 - rep movsb - - } - - Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", - ContextRecord.Eax, ContextRecord.Ebx, ContextRecord.Ecx, - ContextRecord.Edx, ContextRecord.Esi, ContextRecord.Edi, SPVal); - - Debugprintf("Stack:"); - - Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", - SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); - - Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", - SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); - -} - - - -void myInvalidParameterHandler(const wchar_t* expression, - const wchar_t* function, - const wchar_t* file, - unsigned int line, - uintptr_t pReserved) -{ - Logprintf(LOG_DEBUG_X, NULL, '!', "*** Error **** C Run Time Invalid Parameter Handler Called"); - - if (expression && function && file) - { - Logprintf(LOG_DEBUG_X, NULL, '!', "Expression = %S", expression); - Logprintf(LOG_DEBUG_X, NULL, '!', "Function %S", function); - Logprintf(LOG_DEBUG_X, NULL, '!', "File %S Line %d", file, line); - } -} - -// If program gets too many program errors, it will restart itself and shut down - -VOID CheckProgramErrors() -{ - STARTUPINFO SInfo; // pointer to STARTUPINFO - PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION - char ProgName[256]; - - if (Restarting) - exit(0); // Make sure can't loop in restarting - - ProgramErrors++; - - if (ProgramErrors > 25) - { - Restarting = TRUE; - - Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); - - if (cfgMinToTray) - { - DeleteTrayMenuItem(MainWnd); - if (ConsHeader[0]->hConsole) - DeleteTrayMenuItem(ConsHeader[0]->hConsole); - if (ConsHeader[1]->hConsole) - DeleteTrayMenuItem(ConsHeader[1]->hConsole); - if (hMonitor) - DeleteTrayMenuItem(hMonitor); - } - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - GetModuleFileName(NULL, ProgName, 256); - - Debugprintf("Attempting to Restart %s", ProgName); - - CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); - - exit(0); - } -} - - -VOID WriteMiniDump() -{ -#ifdef WIN32 - - HANDLE hFile; - BOOL ret; - char FN[256]; - - sprintf(FN, "%s/Logs/MiniDump%x.dmp", GetBPQDirectory(), time(NULL)); - - hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, - 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) - { - // Create the minidump - - ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), - hFile, MiniDumpNormal, 0, 0, 0 ); - - if(!ret) - Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); - else - Debugprintf("Minidump %s created.", FN); - CloseHandle(hFile); - } -#endif -} - - -void GetSemaphore(struct SEM * Semaphore, int ID) -{ - // - // Wait for it to be free - // -#ifdef WIN32 - if (Semaphore->Flag != 0) - { - Semaphore->Clashes++; - } -loop1: - - while (Semaphore->Flag != 0) - { - Sleep(10); - } - - // - // try to get semaphore - // - - _asm{ - - mov eax,1 - mov ebx, Semaphore - xchg [ebx],eax // this instruction is locked - - cmp eax,0 - jne loop1 // someone else got it - try again -; -; ok, weve got the semaphore -; - } -#else - - while (Semaphore->Flag) - usleep(10000); - - Semaphore->Flag = 1; - -#endif - return; -} - -void FreeSemaphore(struct SEM * Semaphore) -{ - Semaphore->Flag = 0; - - return; -} - -char * CmdLine; - -extern int configSaved; - -int APIENTRY WinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPTSTR lpCmdLine, - int nCmdShow) -{ - MSG msg; - HACCEL hAccelTable; - int BPQStream, n; - struct UserInfo * user; - struct _EXCEPTION_POINTERS exinfo; - _invalid_parameter_handler oldHandler, newHandler; - char Msg[100]; - int i = 60; - struct NNTPRec * NNTPREC; - struct NNTPRec * SaveNNTPREC; - - CmdLine = _strdup(lpCmdLine); - _strlwr(CmdLine); - - if (_stricmp(lpCmdLine, "Wait") == 0) // If AutoRestart then Delay 60 Secs - { - hWnd = CreateWindow("STATIC", "Mail Restarting after Failure - Please Wait", 0, - CW_USEDEFAULT, 100, 550, 70, - NULL, NULL, hInstance, NULL); - - ShowWindow(hWnd, nCmdShow); - - while (i-- > 0) - { - sprintf(Msg, "Mail Restarting after Failure - Please Wait %d secs.", i); - SetWindowText(hWnd, Msg); - - Sleep(1000); - } - - DestroyWindow(hWnd); - } - - __try { - - // Trap CRT Errors - - newHandler = myInvalidParameterHandler; - oldHandler = _set_invalid_parameter_handler(newHandler); - - // Initialize global strings - LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); - LoadString(hInstance, IDC_BPQMailChat, szWindowClass, MAX_LOADSTRING); - MyRegisterClass(hInstance); - - // Perform application initialization: - - if (!InitInstance (hInstance, nCmdShow)) - { - return FALSE; - } - - hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BPQMailChat)); - - // Main message loop: - - Logprintf(LOG_DEBUG_X, NULL, '!', "Program Starting"); - Logprintf(LOG_BBS, NULL, '!', "BPQMail Starting"); - Debugprintf("BPQMail Starting"); - - if (pDllBPQTRACE == 0) - Logprintf(LOG_BBS, NULL, '!', "Remote Monitor Log not available - update BPQ32.dll to enable"); - - - } My__except_Routine("Init"); - - while (GetMessage(&msg, NULL, 0, 0)) - { - __try - { - if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - #define EXCEPTMSG "GetMessageLoop" - #include "StdExcept.c" - - CheckProgramErrors(); - } - } - - __try - { - for (n = 0; n < NumberofStreams; n++) - { - BPQStream=Connections[n].BPQStream; - - if (BPQStream) - { - SetAppl(BPQStream, 0, 0); - Disconnect(BPQStream); - DeallocateStream(BPQStream); - } - } - - - hWnd = CreateWindow("STATIC", "Mail Closing - Please Wait", 0, - 150, 200, 350, 40, NULL, NULL, hInstance, NULL); - - ShowWindow(hWnd, nCmdShow); - - Sleep(1000); // A bit of time for links to close - - DestroyWindow(hWnd); - - if (ConsHeader[0]->hConsole) - DestroyWindow(ConsHeader[0]->hConsole); - if (ConsHeader[1]->hConsole) - DestroyWindow(ConsHeader[1]->hConsole); - if (hMonitor) - { - DestroyWindow(hMonitor); - hMonitor = (HWND)1; // For status Save - } - - -// SaveUserDatabase(); - SaveMessageDatabase(); - SaveBIDDatabase(); - - configSaved = 1; - SaveConfig(ConfigName); - - if (cfgMinToTray) - { - DeleteTrayMenuItem(MainWnd); - if (ConsHeader[0]->hConsole) - DeleteTrayMenuItem(ConsHeader[0]->hConsole); - if (ConsHeader[1]->hConsole) - DeleteTrayMenuItem(ConsHeader[1]->hConsole); - if (hMonitor) - DeleteTrayMenuItem(hMonitor); - } - - // Free all allocated memory - - for (n = 0; n <= NumberofUsers; n++) - { - user = UserRecPtr[n]; - - if (user->ForwardingInfo) - { - FreeForwardingStruct(user); - free(user->ForwardingInfo); - } - - free(user->Temp); - - free(user); - } - - free(UserRecPtr); - - for (n = 0; n <= NumberofMessages; n++) - free(MsgHddrPtr[n]); - - free(MsgHddrPtr); - - for (n = 0; n <= NumberofWPrecs; n++) - free(WPRecPtr[n]); - - free(WPRecPtr); - - for (n = 0; n <= NumberofBIDs; n++) - free(BIDRecPtr[n]); - - free(BIDRecPtr); - - if (TempBIDRecPtr) - free(TempBIDRecPtr); - - NNTPREC = FirstNNTPRec; - - while (NNTPREC) - { - SaveNNTPREC = NNTPREC->Next; - free(NNTPREC); - NNTPREC = SaveNNTPREC; - } - - if (BadWords) free(BadWords); - if (BadFile) free(BadFile); - - n = 0; - - if (Aliases) - { - while(Aliases[n]) - { - free(Aliases[n]->Dest); - free(Aliases[n]); - n++; - } - - free(Aliases); - FreeList(AliasText); - } - - n = 0; - - if (NTSAliases) - { - while(NTSAliases[n]) - { - free(NTSAliases[n]->Dest); - free(NTSAliases[n]); - n++; - } - - free(NTSAliases); - } - - FreeOverrides(); - - FreeList(RejFrom); - FreeList(RejTo); - FreeList(RejAt); - FreeList(RejBID); - FreeList(HoldFrom); - FreeList(HoldTo); - FreeList(HoldAt); - FreeList(HoldBID); - FreeList(SendWPAddrs); - - Free_UI(); - - for (n=1; n<20; n++) - { - if (MyElements[n]) free(MyElements[n]); - } - - free(WelcomeMsg); - free(NewWelcomeMsg); - free(ExpertWelcomeMsg); - - free(Prompt); - free(NewPrompt); - free(ExpertPrompt); - - FreeWebMailMallocs(); - - free(CmdLine); - - _CrtDumpMemoryLeaks(); - - } - My__except_Routine("Close Processing"); - - CloseBPQ32(); // Close Ext Drivers if last bpq32 process - - return (int) msg.wParam; -} - - - -// -// FUNCTION: MyRegisterClass() -// -// PURPOSE: Registers the window class. -// -// COMMENTS: -// -// This function and its usage are only necessary if you want this code -// to be compatible with Win32 systems prior to the 'RegisterClassEx' -// function that was added to Windows 95. It is important to call this function -// so that the application will get 'well formed' small icons associated -// with it. -// -// -#define BGCOLOUR RGB(236,233,216) -//#define BGCOLOUR RGB(245,245,245) - -HBRUSH bgBrush; - -ATOM MyRegisterClass(HINSTANCE hInstance) -{ - WNDCLASSEX wcex; - - bgBrush = CreateSolidBrush(BGCOLOUR); - - wcex.cbSize = sizeof(WNDCLASSEX); - - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = DLGWINDOWEXTRA; - wcex.hInstance = hInstance; - wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(BPQICON)); - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = bgBrush; - wcex.lpszMenuName = MAKEINTRESOURCE(IDC_BPQMailChat); - wcex.lpszClassName = szWindowClass; - wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(BPQICON)); - - return RegisterClassEx(&wcex); -} - - -// -// FUNCTION: InitInstance(HINSTANCE, int) -// -// PURPOSE: Saves instance handle and creates main window -// -// COMMENTS: -// -// In this function, we save the instance handle in a global variable and -// create and display the main program window. -// - -HWND hWnd; - -int AXIPPort = 0; - -char LOC[7] = ""; - -BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) -{ - char Title[80]; - WSADATA WsaData; - HMENU hTopMenu; // handle of menu - HKEY hKey=0; - int retCode; - RECT InitRect; - RECT SessRect; - struct _EXCEPTION_POINTERS exinfo; - - HMODULE ExtDriver = LoadLibrary("bpq32.dll"); - - if (ExtDriver) - { - pDllBPQTRACE = GetProcAddress(ExtDriver,"_DllBPQTRACE@8"); - pGetLOC = GetProcAddress(ExtDriver,"_GetLOC@0"); - pRefreshWebMailIndex = GetProcAddress(ExtDriver,"_RefreshWebMailIndex@0"); - pRunEventProgram = GetProcAddress(ExtDriver,"_RunEventProgram@8"); - - if (pGetLOC) - { - char * pLOC = (char *)pGetLOC(); - memcpy(LOC, pLOC, 6); - } - } - - // See if running under WINE - - retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); - - if (retCode == ERROR_SUCCESS) - { - RegCloseKey(hKey); - WINE =TRUE; - Debugprintf("Running under WINE"); - } - - - REGTREE = GetRegistryKey(); - REGTREETEXT = GetRegistryKeyText(); - - Sleep(1000); - - { - int n; - struct _EXTPORTDATA * PORTVEC; - - KISSOnly = TRUE; - - for (n=1; n <= GetNumberofPorts(); n++) - { - PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(n); - - if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL - { - if (_memicmp(PORTVEC->PORT_DLL_NAME, "TELNET", 6) == 0) - KISSOnly = FALSE; - - if (PORTVEC->PORTCONTROL.PROTOCOL != 10) // Pactor/WINMOR - KISSOnly = FALSE; - - if (AXIPPort == 0) - { - if (_memicmp(PORTVEC->PORT_DLL_NAME, "BPQAXIP", 7) == 0) - { - AXIPPort = PORTVEC->PORTCONTROL.PORTNUMBER; - KISSOnly = FALSE; - } - } - } - } - } - - hInst = hInstance; - - hWnd=CreateDialog(hInst,szWindowClass,0,NULL); - - if (!hWnd) - { - return FALSE; - } - - MainWnd = hWnd; - - GetVersionInfo(NULL); - - sprintf(Title,"G8BPQ Mail Server Version %s", VersionString); - - sprintf(RlineVer, "BPQ%s%d.%d.%d", (KISSOnly) ? "K" : "", Ver[0], Ver[1], Ver[2]); - - SetWindowText(hWnd,Title); - - hWndSess = GetDlgItem(hWnd, 100); - - GetWindowRect(hWnd, &InitRect); - GetWindowRect(hWndSess, &SessRect); - - SessX = SessRect.left - InitRect.left ; - SessY = SessRect.top -InitRect.top; - SessWidth = SessRect.right - SessRect.left; - - // Get handles for updating menu items - - hTopMenu=GetMenu(MainWnd); - hActionMenu=GetSubMenu(hTopMenu,0); - - hFWDMenu=GetSubMenu(hActionMenu,0); - hMenu=GetSubMenu(hActionMenu,1); - hDisMenu=GetSubMenu(hActionMenu,2); - - CheckTimer(); - - cfgMinToTray = GetMinimizetoTrayFlag(); - - if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE)) - if (cfgMinToTray) - { - ShowWindow(hWnd, SW_HIDE); - } - else - { - ShowWindow(hWnd, nCmdShow); - } - else - ShowWindow(hWnd, nCmdShow); - - UpdateWindow(hWnd); - - WSAStartup(MAKEWORD(2, 0), &WsaData); - - __try { - - return Initialise(); - - }My__except_Routine("Initialise"); - - return FALSE; -} - -// -// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) -// -// PURPOSE: Processes messages for the main window. -// -// - - -LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - int wmId, wmEvent; - PAINTSTRUCT ps; - HDC hdc; - int state,change; - ConnectionInfo * conn; - struct _EXCEPTION_POINTERS exinfo; - - - if (message == BPQMsg) - { - if (lParam & BPQMonitorAvail) - { - __try - { - DoBBSMonitorData(wParam); - } - My__except_Routine("DoMonitorData"); - - return 0; - - } - if (lParam & BPQDataAvail) - { - // Dont trap error at this level - let Node error handler pick it up -// __try -// { - DoReceivedData(wParam); -// } -// My__except_Routine("DoReceivedData") - return 0; - } - if (lParam & BPQStateChange) - { - // Get current Session State. Any state changed is ACK'ed - // automatically. See BPQHOST functions 4 and 5. - - __try - { - SessionState(wParam, &state, &change); - - if (change == 1) - { - if (state == 1) // Connected - { - GetSemaphore(&ConSemaphore, 0); - __try {Connected(wParam);} - My__except_Routine("Connected"); - FreeSemaphore(&ConSemaphore); - } - else - { - GetSemaphore(&ConSemaphore, 0); - __try{Disconnected(wParam);} - My__except_Routine("Disconnected"); - FreeSemaphore(&ConSemaphore); - } - } - } - My__except_Routine("DoStateChange"); - - } - - return 0; - } - - - switch (message) - { - - case WM_KEYUP: - - switch (wParam) - { - case VK_F2: - CreateConsole(-1); - return 0; - - case VK_F3: - CreateMulticastConsole(); - return 0; - - case VK_F4: - CreateMonitor(); - return 0; - - case VK_TAB: - return TRUE; - - break; - - - - } - return 0; - - case WM_TIMER: - - if (wParam == 1) // Slow = 10 secs - { - __try - { - time_t NOW = time(NULL); - struct tm * tm; - RefreshMainWindow(); - CheckTimer(); - TCPTimer(); - BBSSlowTimer(); - FWDTimerProc(); - if (MaintClock < NOW) - { - while (MaintClock < NOW) // in case large time step - MaintClock += MaintInterval * 3600; - - Debugprintf("|Enter HouseKeeping"); - DoHouseKeeping(FALSE); - } - tm = gmtime(&NOW); - - if (tm->tm_wday == 0) // Sunday - { - if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) - { - CreateBBSTrafficReport(); - LastTrafficTime = NOW; - } - } - } - My__except_Routine("Slow Timer"); - } - else - __try - { - TrytoSend(); - TCPFastTimer(); - } - My__except_Routine("TrytoSend"); - - return (0); - - - case WM_CTLCOLORDLG: - return (LONG)bgBrush; - - case WM_CTLCOLORSTATIC: - { - HDC hdcStatic = (HDC)wParam; - SetTextColor(hdcStatic, RGB(0, 0, 0)); - SetBkMode(hdcStatic, TRANSPARENT); - return (LONG)bgBrush; - } - - case WM_INITMENUPOPUP: - - if (wParam == (WPARAM)hActionMenu) - { - if (IsClipboardFormatAvailable(CF_TEXT)) - EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_ENABLED); - else - EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_GRAYED ); - - return TRUE; - } - - if (wParam == (WPARAM)hFWDMenu) - { - // Set up Forward Menu - - struct UserInfo * user; - char MenuLine[30]; - - for (user = BBSChain; user; user = user->BBSNext) - { - sprintf(MenuLine, "%s %d Msgs", user->Call, CountMessagestoForward(user)); - - if (ModifyMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, - MF_BYCOMMAND | MF_STRING, IDM_FORWARD_ALL + user->BBSNumber, MenuLine) == 0) - - AppendMenu(hFWDMenu, MF_STRING,IDM_FORWARD_ALL + user->BBSNumber, MenuLine); - } - return TRUE; - } - - if (wParam == (WPARAM)hDisMenu) - { - // Set up Disconnect Menu - - CIRCUIT * conn; - char MenuLine[30]; - int n; - - for (n = 0; n <= NumberofStreams-1; n++) - { - conn=&Connections[n]; - - RemoveMenu(hDisMenu, IDM_DISCONNECT + n, MF_BYCOMMAND); - - if (conn->Active) - { - sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign); - AppendMenu(hDisMenu, MF_STRING, IDM_DISCONNECT + n, MenuLine); - } - } - return TRUE; - } - break; - - - case WM_COMMAND: - wmId = LOWORD(wParam); - wmEvent = HIWORD(wParam); - // Parse the menu selections: - - if (wmEvent == LBN_DBLCLK) - - break; - - if (wmId >= IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1) - { - // disconnect user - - conn=&Connections[wmId-IDM_DISCONNECT]; - - if (conn->Active) - { - Disconnect(conn->BPQStream); - } - } - - if (wmId >= IDM_FORWARD_ALL && wmId < IDM_FORWARD_ALL + 100) - { - StartForwarding(wmId - IDM_FORWARD_ALL, NULL); - return 0; - } - - switch (wmId) - { - case IDM_LOGBBS: - - ToggleParam(hMenu, hWnd, &LogBBS, IDM_LOGBBS); - break; - - case IDM_LOGCHAT: - - ToggleParam(hMenu, hWnd, &LogCHAT, IDM_LOGCHAT); - break; - - case IDM_LOGTCP: - - ToggleParam(hMenu, hWnd, &LogTCP, IDM_LOGTCP); - break; - - case IDM_HOUSEKEEPING: - - DoHouseKeeping(TRUE); - - break; - - case IDM_CONSOLE: - - CreateConsole(-1); - break; - - case IDM_MCMONITOR: - - CreateMulticastConsole(); - break; - - case IDM_MONITOR: - - CreateMonitor(); - break; - - case RESCANMSGS: - - ReRouteMessages(); - break; - - case IDM_IMPORT: - - ImportMessages(NULL, "", FALSE); - break; - - case IDM_ABOUT: - DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); - break; - - case ID_HELP_ONLINEHELP: - - ShellExecute(hWnd,"open", - "http://www.cantab.net/users/john.wiseman/Documents/MailServer.html", - "", NULL, SW_SHOWNORMAL); - - break; - - case IDM_CONFIG: - DialogBox(hInst, MAKEINTRESOURCE(IDD_CONFIG), hWnd, ConfigWndProc); - break; - - case IDM_USERS: - DialogBox(hInst, MAKEINTRESOURCE(IDD_USEREDIT), hWnd, UserEditDialogProc); - break; - - case IDM_FWD: - DialogBox(hInst, MAKEINTRESOURCE(IDD_FORWARDING), hWnd, FwdEditDialogProc); - break; - - case IDM_MESSAGES: - DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGEDIT), hWnd, MsgEditDialogProc); - break; - - case IDM_WP: - DialogBox(hInst, MAKEINTRESOURCE(IDD_EDITWP), hWnd, WPEditDialogProc); - break; - - case ID_ACTIONS_SENDMSGFROMCLIPBOARD: - DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, ClpMsgDialogProc); - break; - - case ID_ACTIONS_SENDMESSAGE: - DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, SendMsgDialogProc); - break; - - case ID_MULTICAST: - - MulticastRX = !MulticastRX; - CheckMenuItem(hActionMenu, ID_MULTICAST, (MulticastRX) ? MF_CHECKED : MF_UNCHECKED); - break; - - case IDM_EXIT: - DestroyWindow(hWnd); - break; - - - - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - break; - - case WM_SIZE: - - if (wParam == SIZE_MINIMIZED) - if (cfgMinToTray) - return ShowWindow(hWnd, SW_HIDE); - - return (0); - - - case WM_SIZING: - { - LPRECT lprc = (LPRECT) lParam; - int Height = lprc->bottom-lprc->top; - int Width = lprc->right-lprc->left; - - MoveWindow(hWndSess, 0, 30, SessWidth, Height - 100, TRUE); - - return TRUE; - } - - - case WM_PAINT: - hdc = BeginPaint(hWnd, &ps); - // TODO: Add any drawing code here... - EndPaint(hWnd, &ps); - break; - - case WM_DESTROY: - - GetWindowRect(MainWnd, &MainRect); // For save soutine - if (ConsHeader[0]->hConsole) - GetWindowRect(ConsHeader[0]->hConsole, &ConsHeader[0]->ConsoleRect); // For save soutine - if (ConsHeader[1]->hConsole) - GetWindowRect(ConsHeader[1]->hConsole, &ConsHeader[1]->ConsoleRect); // For save soutine - if (hMonitor) - GetWindowRect(hMonitor, &MonitorRect); // For save soutine - - KillTimer(hWnd,1); - KillTimer(hWnd,2); - PostQuitMessage(0); - break; - - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - return 0; -} - -INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_INITDIALOG: - - SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B"); - SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P"); - SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "T"); - - SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 0, 0); - - return TRUE; - - case WM_SIZING: - { - HWND hWndEdit = GetDlgItem(hDlg, IDC_EDIT1); - - LPRECT lprc = (LPRECT) lParam; - int Height = lprc->bottom-lprc->top; - int Width = lprc->right-lprc->left; - - MoveWindow(hWndEdit, 5, 90, Width-20, Height - 140, TRUE); - - return TRUE; - } - - case WM_COMMAND: - - if (LOWORD(wParam) == IDSEND) - { - char status [3]; - struct MsgInfo * Msg; - char * via = NULL; - char BID[13]; - char FileList[32768]; - BIDRec * BIDRec; - int MsgLen; - char * MailBuffer; - char MsgFile[MAX_PATH]; - HANDLE hFile = INVALID_HANDLE_VALUE; - int WriteLen=0; - char HDest[61]; - char Destcopy[61]; - char * Vptr; - char * FileName[100]; - int FileLen[100]; - char * FileBody[100]; - int n, Files = 0; - int TotalFileSize = 0; - char * NewMsg; - - GetDlgItemText(hDlg, IDC_MSGTO, HDest, 60); - strcpy(Destcopy, HDest); - - GetDlgItemText(hDlg, IDC_MSGBID, BID, 13); - strlop(BID, ' '); - - GetDlgItemText(hDlg, IDC_ATTACHMENTS, FileList, 32767); - - // if there are attachments, check that they can be opened ane read - - n = 0; - - if (FileList[0]) - { - FILE * Handle; - struct stat STAT; - char * ptr1 = FileList, * ptr2; - - while(ptr1 && ptr1[0]) - { - ptr2 = strchr(ptr1, ';'); - - if (ptr2) - *(ptr2++) = 0; - - FileName[n++] = ptr1; - - ptr1 = ptr2; - } - - FileName[n] = 0; - - // read the files - - Files = n; - n = 0; - - while (FileName[n]) - { - if (stat(FileName[n], &STAT) == -1) - { - char ErrorMessage[512]; - sprintf(ErrorMessage,"Can't find file %s", FileName[n]); - MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); - return TRUE; - } - - FileLen[n] = STAT.st_size; - - Handle = fopen(FileName[n], "rb"); - - if (Handle == NULL) - { - char ErrorMessage[512]; - sprintf(ErrorMessage,"Can't open file %s", FileName[n]); - MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); - return TRUE; - } - - FileBody[n] = malloc(FileLen[n]+1); - - fread(FileBody[n], 1, FileLen[n], Handle); - - fclose(Handle); - - TotalFileSize += FileLen[n]; - n++; - } - } - - if (strlen(HDest) == 0) - { - MessageBox(NULL, "To: Call Missing!", "BPQMail", MB_ICONERROR); - return TRUE; - } - - if (strlen(BID)) - { - if (LookupBID(BID)) - { - // Duplicate bid - - MessageBox(NULL, "Duplicate BID", "BPQMail", MB_ICONERROR); - return TRUE; - } - } - - Msg = AllocateMsgRecord(); - - // Set number here so they remain in sequence - - Msg->number = ++LatestMsg; - MsgnotoMsg[Msg->number] = Msg; - - strcpy(Msg->from, SYSOPCall); - - Vptr = strlop(Destcopy, '@'); - - if (Vptr == 0 && strchr(Destcopy, '!')) // Bang route without @ - { - Vptr = strchr(Destcopy, '!'); - strcpy(Msg->via, Vptr); - strlop(Destcopy, '!'); - - if (strlen(Destcopy) > 6) - memcpy(Msg->to, Destcopy, 6); - else - strcpy(Msg->to, Destcopy); - goto gotAddr; - } - - if (strlen(Destcopy) > 6) - memcpy(Msg->to, Destcopy, 6); - else - strcpy(Msg->to, Destcopy); - - _strupr(Msg->to); - - if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) - { - Vptr = HDest; - memmove(HDest, &HDest[4], strlen(HDest)); - strcpy(Msg->to, "RMS"); - - } - else if (_memicmp(HDest, "smtp:", 5) == 0) - { - if (ISP_Gateway_Enabled) - { - Vptr = HDest; - memmove(HDest, &HDest[5], strlen(HDest)); - Msg->to[0] = 0; - } - } - else if (Vptr) - { - // If looks like a valid email address, treat as such - - int tolen = (Vptr - Destcopy) - 1; - - if (tolen > 6 || !CheckifPacket(Vptr)) - { - // Assume Email address - - Vptr = HDest; - - if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route - strcpy(Msg->to, "RMS"); - else if (ISP_Gateway_Enabled) - Msg->to[0] = 0; - else - { - MessageBox(NULL, "Sending to Internet Email not available", "BPQMail", MB_ICONERROR); - return TRUE; - } - } - } - if (Vptr) - { - if (strlen(Vptr) > 40) - Vptr[40] = 0; - - strcpy(Msg->via, Vptr); - } -gotAddr: - GetDlgItemText(hDlg, IDC_MSGTITLE, Msg->title, 61); - GetDlgItemText(hDlg, IDC_MSGTYPE, status, 2); - Msg->type = status[0]; - Msg->status = 'N'; - - if (strlen(BID) == 0) - sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); - - strcpy(Msg->bid, BID); - - Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); - - BIDRec = AllocateBIDRecord(); - - strcpy(BIDRec->BID, Msg->bid); - BIDRec->mode = Msg->type; - BIDRec->u.msgno = LOWORD(Msg->number); - BIDRec->u.timestamp = LOWORD(time(NULL)/86400); - - MsgLen = SendDlgItemMessage(hDlg, IDC_EDIT1, WM_GETTEXTLENGTH, 0 ,0); - - MailBuffer = malloc(MsgLen + TotalFileSize + 2000); // Allow for a B2 Header if attachments - - if (Files) - { - char DateString[80]; - struct tm * tm; - - char Type[16] = "Private"; - - // Get Type - - if (Msg->type == 'B') - strcpy(Type, "Bulletin"); - else if (Msg->type == 'T') - strcpy(Type, "Traffic"); - - // Create a B2 Message - - // B2 Header - - NewMsg = MailBuffer + 1000; - - tm = gmtime((time_t *)&Msg->datecreated); - - sprintf(DateString, "%04d/%02d/%02d %02d:%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); - - // Remove last Source Route - - if (strchr(HDest, '!')) - { - char * bang = HDest + strlen(HDest); - - while (*(--bang) != '!'); // Find last ! - - *(bang) = 0; // remove it; - } - - NewMsg += sprintf(NewMsg, - "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n", - Msg->bid, DateString, Type, Msg->from, HDest, Msg->title, BBSName); - - - NewMsg += sprintf(NewMsg, "Body: %d\r\n", MsgLen); - - for (n = 0; n < Files; n++) - { - char * p = FileName[n], * q; - - // Remove any path - - q = strchr(p, '\\'); - - while (q) - { - if (q) - *q++ = 0; - p = q; - q = strchr(p, '\\'); - } - - NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p); - } - - NewMsg += sprintf(NewMsg, "\r\n"); - GetDlgItemText(hDlg, IDC_EDIT1, NewMsg, MsgLen+1); - NewMsg += MsgLen; - NewMsg += sprintf(NewMsg, "\r\n"); - - for (n = 0; n < Files; n++) - { - memcpy(NewMsg, FileBody[n], FileLen[n]); - NewMsg += FileLen[n]; - free(FileBody[n]); - NewMsg += sprintf(NewMsg, "\r\n"); - } - - Msg->length = NewMsg - (MailBuffer + 1000); - NewMsg = MailBuffer + 1000; - Msg->B2Flags = B2Msg | Attachments; - } - - else - { - GetDlgItemText(hDlg, IDC_EDIT1, MailBuffer, MsgLen+1); - Msg->length = MsgLen; - NewMsg = MailBuffer; - } - - sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); - - hFile = CreateFile(MsgFile, - GENERIC_WRITE, - FILE_SHARE_READ, - NULL, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (hFile != INVALID_HANDLE_VALUE) - { - WriteFile(hFile, NewMsg, Msg->length, &WriteLen, NULL); - CloseHandle(hFile); - } - - free(MailBuffer); - - MatchMessagetoBBSList(Msg, 0); - - BuildNNTPList(Msg); // Build NNTP Groups list - - SaveMessageDatabase(); - SaveBIDDatabase(); - - EndDialog(hDlg, LOWORD(wParam)); - - return TRUE; - } - - - if (LOWORD(wParam) == IDSelectFiles) - { - char FileNames[2048]; - char FullFileNames[32768]; - OPENFILENAME Ofn; - int err; - - FileNames[0] = 0; - - memset(&Ofn, 0, sizeof(Ofn)); - - Ofn.lStructSize = sizeof(OPENFILENAME); - Ofn.hInstance = hInst; - Ofn.hwndOwner = hDlg; - Ofn.lpstrFilter = NULL; - Ofn.lpstrFile= FileNames; - Ofn.nMaxFile = 2048; - Ofn.lpstrFileTitle = NULL; - Ofn.nMaxFileTitle = 0; - Ofn.lpstrInitialDir = (LPSTR)NULL; - Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; - Ofn.lpstrTitle = NULL;//; - - if (GetOpenFileName(&Ofn)) - { - // if one is selected, a single string is returned, if more than one, a single - // path, followed by all the strings, duuble null terminated. - - char * Names[101]; // Allow up to 100 names - int n = 0; - char * ptr = FileNames; - - while (*ptr) - { - Names[n++] = ptr; - ptr += strlen(ptr); - ptr++; - } - - GetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames, 32768); - - if (strlen(FullFileNames)) - strcat(FullFileNames, ";"); - - if (n == 1) - { - // Single Select - - strcat(FullFileNames, FileNames); - } - else - { - int i = 1; - - while(i < n) - { - strcat(FullFileNames, Names[0]); - strcat(FullFileNames, "\\"); - strcat(FullFileNames, Names[i]); - i++; - if (i < n) - strcat(FullFileNames, ";"); - } - } - SetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames); - } - else - err = GetLastError(); - return (INT_PTR)TRUE; - } - - - if (LOWORD(wParam) == IDCANCEL) - { - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - } - - return (INT_PTR)TRUE; - - break; - } - return (INT_PTR)FALSE; -} - -INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - HGLOBAL hglb; - LPTSTR lptstr; - - switch (message) - { - case WM_INITDIALOG: - - SetWindowText(hDlg, "Send Message from Clipboard"); - - if (!IsClipboardFormatAvailable(CF_TEXT)) - break; - - if (!OpenClipboard(hDlg)) - break; - - hglb = GetClipboardData(CF_TEXT); - - if (hglb != NULL) - { - lptstr = GlobalLock(hglb); - - if (lptstr != NULL) - { - SetDlgItemText(hDlg, IDC_EDIT1, lptstr); - GlobalUnlock(hglb); - } - } - CloseClipboard(); - } - - return SendMsgDialogProc(hDlg, message, wParam, lParam); - -} - -// Message handler for about box. -INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - UNREFERENCED_PARAMETER(lParam); - switch (message) - { - case WM_INITDIALOG: - return (INT_PTR)TRUE; - - case WM_COMMAND: - if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) - { - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - } - return (INT_PTR)TRUE; - - break; - } - return (INT_PTR)FALSE; -} - -SMTPMsgs = 0; - -int RefreshMainWindow() -{ - char msg[80]; - CIRCUIT * conn; - int i,n, SYSOPMsgs = 0, HeldMsgs = 0; - time_t now; - struct tm * tm; - char tim[20]; - - SendDlgItemMessage(MainWnd,100,LB_RESETCONTENT,0,0); - - SMTPMsgs = 0; - - for (n = 0; n < NumberofStreams; n++) - { - conn=&Connections[n]; - - if (!conn->Active) - { - strcpy(msg,"Idle"); - } - else - { - { - if (conn->UserPointer == 0) - strcpy(msg,"Logging in"); - else - { - i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d", - conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream, - "BBS", conn->OutputQueueLength - conn->OutputGetPointer); - } - } - } - SendDlgItemMessage(MainWnd,100,LB_ADDSTRING,0,(LPARAM)msg); - } - - SetDlgItemInt(hWnd, IDC_MSGS, NumberofMessages, FALSE); - - n = 0; - - for (i=1; i <= NumberofMessages; i++) - { - if (MsgHddrPtr[i]->status == 'N') - { - if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0) - SYSOPMsgs++; - else - if (MsgHddrPtr[i]->to[0] == 0) - SMTPMsgs++; - } - else - { - if (MsgHddrPtr[i]->status == 'H') - HeldMsgs++; - } - } - - SetDlgItemInt(hWnd, IDC_SYSOPMSGS, SYSOPMsgs, FALSE); - SetDlgItemInt(hWnd, IDC_HELD, HeldMsgs, FALSE); - SetDlgItemInt(hWnd, IDC_SMTP, SMTPMsgs, FALSE); - - SetDlgItemInt(hWnd, IDC_MSGSEM, MsgNoSemaphore.Clashes, FALSE); - SetDlgItemInt(hWnd, IDC_ALLOCSEM, AllocSemaphore.Clashes, FALSE); - SetDlgItemInt(hWnd, IDC_CONSEM, ConSemaphore.Clashes, FALSE); - - now = time(NULL); - - tm = gmtime(&now); - sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); - SetDlgItemText(hWnd, IDC_UTC, tim); - - tm = localtime(&now); - sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); - SetDlgItemText(hWnd, IDC_LOCAL, tim); - - - return 0; -} - -#define MAX_PENDING_CONNECTS 4 - -#define VERSION_MAJOR 2 -#define VERSION_MINOR 0 - -SOCKADDR_IN local_sin; /* Local socket - internet style */ - -PSOCKADDR_IN psin; - -SOCKET sock; - - - -BOOL Initialise() -{ - int i, len; - ConnectionInfo * conn; - struct UserInfo * user = NULL; - HKEY hKey=0; - char * ptr1; - int Attrs, ret; - char msg[500]; - TIME_ZONE_INFORMATION TimeZoneInformation; - struct stat STAT; - - GetTimeZoneInformation(&TimeZoneInformation); - - _tzset(); - _MYTIMEZONE = timezone; - _MYTIMEZONE = TimeZoneInformation.Bias * 60; - - // Register message for posting by BPQDLL - - BPQMsg = RegisterWindowMessage(BPQWinMsg); - - // See if we need to warn of possible problem with BaseDir moved by installer - - strcpy(BPQDirectory, GetBPQDirectory()); - - sprintf(BaseDir, "%s/BPQMailChat", BPQDirectory); - - len = strlen(BaseDir); - ptr1 = BaseDir; - - while (*ptr1) - { - if (*(ptr1) == '/') *(ptr1) = '\\'; - ptr1++; - } - - // Make Sure BASEDIR Exists - - Attrs = GetFileAttributes(BaseDir); - - if (Attrs == -1) - { - sprintf_s(msg, sizeof(msg), "Base Directory %s not found - should it be created?", BaseDir); - ret = MessageBox(NULL, msg, "BPQMail", MB_YESNO); - - if (ret == IDYES) - { - ret = CreateDirectory(BaseDir, NULL); - if (ret == 0) - { - MessageBox(NULL, "Failed to created Base Directory - exiting", "BPQMail", MB_ICONSTOP); - return FALSE; - } - } - else - { - MessageBox(NULL, "Can't Continue without a Base Directory - exiting", "BPQMailChat", MB_ICONSTOP); - return FALSE; - } - } - else - { - if (!(Attrs & FILE_ATTRIBUTE_DIRECTORY)) - { - sprintf_s(msg, sizeof(msg), "Base Directory %s is a file not a directory - exiting", BaseDir); - ret = MessageBox(NULL, msg, "BPQMail", MB_ICONSTOP); - - return FALSE; - } - } - - initUTF8(); - - // Set up file and directory names - - strcpy(UserDatabasePath, BaseDir); - strcat(UserDatabasePath, "\\"); - strcat(UserDatabasePath, UserDatabaseName); - - strcpy(MsgDatabasePath, BaseDir); - strcat(MsgDatabasePath, "\\"); - strcat(MsgDatabasePath, MsgDatabaseName); - - strcpy(BIDDatabasePath, BaseDir); - strcat(BIDDatabasePath, "\\"); - strcat(BIDDatabasePath, BIDDatabaseName); - - strcpy(WPDatabasePath, BaseDir); - strcat(WPDatabasePath, "\\"); - strcat(WPDatabasePath, WPDatabaseName); - - strcpy(BadWordsPath, BaseDir); - strcat(BadWordsPath, "\\"); - strcat(BadWordsPath, BadWordsName); - - strcpy(NTSAliasesPath, BaseDir); - strcat(NTSAliasesPath, "/"); - strcat(NTSAliasesPath, NTSAliasesName); - - strcpy(MailDir, BaseDir); - strcat(MailDir, "\\"); - strcat(MailDir, "Mail"); - - CreateDirectory(MailDir, NULL); // Just in case - - strcpy(ConfigName, BaseDir); - strcat(ConfigName, "\\"); - strcat(ConfigName, "BPQMail.cfg"); - - UsingingRegConfig = FALSE; - - // if config file exists use it else try to get from Registry - - if (stat(ConfigName, &STAT) == -1) - { - UsingingRegConfig = TRUE; - - if (GetConfigFromRegistry()) - { - SaveConfig(ConfigName); - } - else - { - int retCode; - - strcpy(BBSName, GetNodeCall()); - strlop(BBSName, '-'); - strlop(BBSName, ' '); - - sprintf(msg, "No configuration found - Dummy Config created"); - - retCode = MessageBox(NULL, msg, "BPQMailChat", MB_OKCANCEL); - - if (retCode == IDCANCEL) - return FALSE; - - SaveConfig(ConfigName); - } - } - - if (GetConfig(ConfigName) == EXIT_FAILURE) - { - ret = MessageBox(NULL, - "BBS Config File seems corrupt - check before continuing", "BPQMail", MB_ICONSTOP); - return FALSE; - } - - // Got a Config File - - if (MainRect.right < 100 || MainRect.bottom < 100) - { - GetWindowRect(MainWnd, &MainRect); - } - - MoveWindow(MainWnd, MainRect.left, MainRect.top, MainRect.right-MainRect.left, MainRect.bottom-MainRect.top, TRUE); - - if (OpenMon) - CreateMonitor(); - - BBSApplMask = 1<<(BBSApplNum-1); - - ShowWindow(GetDlgItem(MainWnd, 901), SW_HIDE); - ShowWindow(GetDlgItem(MainWnd, 902), SW_HIDE); - ShowWindow(GetDlgItem(MainWnd, 903), SW_HIDE); - - // Make backup copies of Databases - - CopyBIDDatabase(); - CopyMessageDatabase(); - CopyUserDatabase(); - CopyWPDatabase(); - - SetupMyHA(); - SetupFwdAliases(); - SetupNTSAliases(NTSAliasesPath); - - GetWPDatabase(); - GetMessageDatabase(); - GetUserDatabase(); - GetBIDDatabase(); - GetBadWordFile(); - GetHTMLForms(); - - UsingingRegConfig = FALSE; - - // Make sure SYSOPCALL is set - - if (SYSOPCall[0] == 0) - strcpy(SYSOPCall, BBSName); - - // Make sure there is a user record for the BBS, with BBS bit set. - - user = LookupCall(BBSName); - - if (user == NULL) - { - user = AllocateUserRecord(BBSName); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - } - - if ((user->flags & F_BBS) == 0) - { - // Not Defined as a BBS - - if (SetupNewBBS(user)) - user->flags |= F_BBS; - } - - // if forwarding AMPR mail make sure User/BBS AMPR exists - - if (SendAMPRDirect) - { - BOOL NeedSave = FALSE; - - user = LookupCall("AMPR"); - - if (user == NULL) - { - user = AllocateUserRecord("AMPR"); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - NeedSave = TRUE; - } - - if ((user->flags & F_BBS) == 0) - { - // Not Defined as a BBS - - if (SetupNewBBS(user)) - user->flags |= F_BBS; - NeedSave = TRUE; - } - - if (NeedSave) - SaveUserDatabase(); - } - - // Allocate Streams - - for (i=0; i < MaxStreams; i++) - { - conn = &Connections[i]; - conn->BPQStream = FindFreeStream(); - - if (conn->BPQStream == 255) break; - - NumberofStreams++; - - BPQSetHandle(conn->BPQStream, hWnd); - - SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask | ChatApplMask); - Disconnect(conn->BPQStream); - } - - InitialiseTCP(); - - InitialiseNNTP(); - - SetupListenSet(); // Master set of listening sockets - - if (BBSApplNum) - { - SetupUIInterface(); - if (MailForInterval) - _beginthread(SendMailForThread, 0, 0); - } - - if (cfgMinToTray) - { - AddTrayMenuItem(MainWnd, "Mail Server"); - } - - SetTimer(hWnd,1,10000,NULL); // Slow Timer (10 Secs) - SetTimer(hWnd,2,100,NULL); // Send to Node and TCP Poll (100 ms) - - // Calulate time to run Housekeeping - { - struct tm *tm; - time_t now; - - now = time(NULL); - - tm = gmtime(&now); - - tm->tm_hour = MaintTime / 100; - tm->tm_min = MaintTime % 100; - tm->tm_sec = 0; - - MaintClock = _mkgmtime(tm); - - while (MaintClock < now) - MaintClock += MaintInterval * 3600; - - Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); - - if (LastHouseKeepingTime) - { - if ((now - LastHouseKeepingTime) > MaintInterval * 3600) - { - DoHouseKeeping(FALSE); - } - } - } - - if (strstr(CmdLine, "tidymail")) - DeleteRedundantMessages(); - - if (strstr(CmdLine, "nohomebbs")) - DontNeedHomeBBS = TRUE; - - if (strstr(CmdLine, "DontCheckFromCall")) - DontCheckFromCall = TRUE; - - CheckMenuItem(hMenu,IDM_LOGBBS, (LogBBS) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hMenu,IDM_LOGTCP, (LogTCP) ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hMenu,IDM_LOGCHAT, (LogCHAT) ? MF_CHECKED : MF_UNCHECKED); - - RefreshMainWindow(); - -// CreateWPReport(); - - CreatePipeThread(); - - return TRUE; -} - -int ConnectState(Stream) -{ - int state; - - SessionStateNoAck(Stream, &state); - return state; -} -UCHAR * EncodeCall(UCHAR * Call) -{ - static char axcall[10]; - - ConvToAX25(Call, axcall); - return &axcall[0]; - -} - -/* -VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo) -{ - struct UserInfo * user; - - int i = FWDInfo->UserIndex; - - if (i == -1) - { - FWDInfo->UserIndex = FWDInfo->UserCall[0] = 0; // Not scanning users - } - - for (i++; i <= NumberofUsers; i++) - { - user = UserRecPtr[i]; - - if (user->flags & F_POLLRMS) - { - FWDInfo->UserIndex = i; - strcpy(FWDInfo->UserCall, user->Call); - FWDInfo->FwdTimer = FWDInfo->FwdInterval - 20; - return ; - } - } - - // Finished Scan - - FWDInfo->UserIndex = FWDInfo->FwdTimer = FWDInfo->UserCall[0] = 0; -} -*/ - -#ifndef NEWROUTING - -VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo) -{ -} -VOID SetupMyHA() -{ -} -VOID SetupFwdAliases() -{ -} - -int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn) -{ - struct UserInfo * bbs; - struct BBSForwardingInfo * ForwardingInfo; - char ATBBS[41]; - char * HRoute; - int Count =0; - - strcpy(ATBBS, Msg->via); - HRoute = strlop(ATBBS, '.'); - - if (Msg->type == 'P') - { - // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - ForwardingInfo = bbs->ForwardingInfo; - - if (CheckBBSToList(Msg, bbs, ForwardingInfo)) - { - if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! - { - if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back - { - set_fwd_bit(Msg->fbbs, bbs->BBSNumber); - ForwardingInfo->MsgCount++; - } - } - return 1; - } - } - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - ForwardingInfo = bbs->ForwardingInfo; - - if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) - { - if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! - { - if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back - { - set_fwd_bit(Msg->fbbs, bbs->BBSNumber); - ForwardingInfo->MsgCount++; - } - } - return 1; - } - } - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - ForwardingInfo = bbs->ForwardingInfo; - - if (CheckBBSHList(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) - { - if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! - { - if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back - { - set_fwd_bit(Msg->fbbs, bbs->BBSNumber); - ForwardingInfo->MsgCount++; - } - } - return 1; - } - } - - return FALSE; - } - - // Bulls go to all matching BBSs, so the order of checking doesn't matter - - for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) - { - ForwardingInfo = bbs->ForwardingInfo; - - if (CheckABBS(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) - { - if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! - { - if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back - { - set_fwd_bit(Msg->fbbs, bbs->BBSNumber); - ForwardingInfo->MsgCount++; - } - } - Count++; - } - } - - return Count; -} -BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) -{ - char ** Calls; - char ** HRoutes; - int i, j; - - if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS - return TRUE; - - // Check TO distributions - - if (ForwardingInfo->TOCalls) - { - Calls = ForwardingInfo->TOCalls; - - while(Calls[0]) - { - if (strcmp(Calls[0], Msg->to) == 0) - return TRUE; - - Calls++; - } - } - - // Check AT distributions - - if (ForwardingInfo->ATCalls) - { - Calls = ForwardingInfo->ATCalls; - - while(Calls[0]) - { - if (strcmp(Calls[0], ATBBS) == 0) - return TRUE; - - Calls++; - } - } - if ((HRoute) && (ForwardingInfo->Haddresses)) - { - // Match on Routes - - HRoutes = ForwardingInfo->Haddresses; - - while(HRoutes[0]) - { - i = strlen(HRoutes[0]) - 1; - j = strlen(HRoute) - 1; - - while ((i >= 0) && (j >= 0)) // Until one string rus out - { - if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards - goto next; - } - - return TRUE; - next: - HRoutes++; - } - } - - - return FALSE; - -} - -BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo) -{ - char ** Calls; - - // Check TO distributions - - if (ForwardingInfo->TOCalls) - { - Calls = ForwardingInfo->TOCalls; - - while(Calls[0]) - { - if (strcmp(Calls[0], Msg->to) == 0) - return TRUE; - - Calls++; - } - } - return FALSE; -} - -BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) -{ - char ** Calls; - - // Check AT distributions - - if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS - return TRUE; - - if (ForwardingInfo->ATCalls) - { - Calls = ForwardingInfo->ATCalls; - - while(Calls[0]) - { - if (strcmp(Calls[0], ATBBS) == 0) - return TRUE; - - Calls++; - } - } - return FALSE; -} - -BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) -{ - char ** HRoutes; - int i, j; - - if ((HRoute) && (ForwardingInfo->Haddresses)) - { - // Match on Routes - - HRoutes = ForwardingInfo->Haddresses; - - while(HRoutes[0]) - { - i = strlen(HRoutes[0]) - 1; - j = strlen(HRoute) - 1; - - while ((i >= 0) && (j >= 0)) // Until one string rus out - { - if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards - goto next; - } - - return TRUE; - next: - HRoutes++; - } - } - return FALSE; -} - -#endif - -char * strlop(char * buf, char delim) -{ - // Terminate buf at delim, and return rest of string - - char * ptr; - - if (buf == NULL) return NULL; // Protect - - ptr = strchr(buf, delim); - - if (ptr == NULL) return NULL; - - *(ptr)++=0; - - return ptr; -} +// Mail and Chat Server for BPQ32 Packet Switch +// +// + +// Version 1.0.0.17re + +// Split Messasge, User and BBS Editing from Main Config. +// Add word wrap to Console input and output +// Flash Console on chat user connect +// Fix processing Name response in chat mode +// Fix processing of *RTL from station not defined as a Chat Node +// Fix overlength lines ln List responses +// Housekeeping expires BIDs +// Killing a message removes it from the forwarding counts + +// Version 1.0.0.18 + +// Save User Database when name is entered or updated so it is not lost on a crash +// Fix Protocol Error in Compressed Forwarding when switching direction +// Add Housekeeping results dialog. + +// Version 1.0.0.19 + +// Allow PACLEN in forward scripts. +// Store and forward messages with CRLF as line ends +// Send Disconnect after FQ ( for LinFBB) +// "Last Listed" is saved if MailChat is closed without closing Console +// Maximum acceptable message length can be specified (in Forwarding Config) + +// Version 1.0.0.20 + +// Fix error in saving forwarding config (introduced in .19) +// Limit size of FBB forwarding block. +// Clear old connection (instead of new) if duplicate connect on Chat Node-Node link +// Send FA for Compressed Mail (was sending FB for both Compressed and Uncompressed) + +// Version 1.0.0.21 + +// Fix Connect Script Processing (wasn't waiting for CONNECTED from last step) +// Implement Defer +// Fix MBL-style forwarding +// Fix Add User (Params were not saved) +// Add SC (Send Copy) Command +// Accept call@bbs as well as call @ bbs + +// Version 1.0.0.22 + +// Implement RB RP LN LR LF LN L$ Commands. +// Implement QTH and ZIP Commands. +// Entering an empty Title cancels the message. +// Uses HomeBBS field to set @ field for local users. +// Creates basic WP Database. +// Uses WP to lookup @ field for non-local calls. +// Console "Actions" Menu renamed "Options". +// Excluded flag is actioned. +// Asks user to set HomeBBS if not already set. +// Fix "Shrinking Message" problem, where message got shorter each time it was read Initroduced in .19). +// Flash Server window when anyone connects to chat (If Console Option "Flash on Chat User Connect" set). + +// Version 1.0.0.23 + +// Fix R: line scan bug + +// Version 1.0.0.24 + +// Fix closing console window on 'B'. +// Fix Message Creation time. +// Enable Delete function in WP edit dialog + +// Version 1.0.0.25 + +// Implement K< and K> commands +// Experimental support for B1 and B2 forwarding +// Experimental UI System +// Fix extracting QTH from WP updates + +// Version 1.0.0.26 + +// Add YN etc responses for FBB B1/B2 + +// Version 1.0.0.27 + +// Fix crash if NULL received as start of a packet. +// Add Save WP command +// Make B2 flag BBS-specific. +// Implement B2 Send + +// Version 1.0.0.28 + +// Fix parsing of smtp to addresses - eg smtp:john.wiseman@cantab.net +// Flag messages as Held if smtp server rejects from or to addresses +// Fix Kill to (K> Call) +// Edit Message dialog shows latest first +// Add chat debug window to try to track down occasional chat connection problems + +// Version 1.0.0.29 + +// Add loads of try/excspt + +// Version 1.0.0.30 + +// Writes Debug output to LOG_DEBUG_X and Monitor Window + +// Version 1.0.0.32 + +// Allow use of GoogleMail for ISP functions +// Accept SYSOP as alias for SYSOPCall - ie user can do SP SYSOP, and it will appear in sysop's LM, RM, etc +// Email Housekeeping Results to SYSOP + +// Version 1.0.0.33 + +// Housekeeping now runs at Maintenance Time. Maintenance Interval removed. +// Allow multiple numbers on R and K commands +// Fix L command with single number +// Log if Forward count is out of step with messages to forward. +// UI Processing improved and F< command implemented + +// Version 1.0.0.34 + +// Semaphore Chat Messages +// Display Semaphore Clashes +// More Program Error Traps +// Kill Messages more than BIDLifetime old + +// Version 1.0.0.35 + +// Test for Mike - Remove B1 check from Parse_SID + +// Version 1.0.0.36 + +// Fix calculation of Housekeeping Time. +// Set dialog box background explicitly. +// Remove tray entry for chat debug window. +// Add date to log file name. +// Add Actions Menu option to disable logging. +// Fix size of main window when it changes between versions. + +// Version 1.0.0.37 + +// Implement Paging. +// Fix L< command (was giving no messages). +// Implement LR LR mmm-nnn LR nnn- (and L nnn-) +// KM should no longer kill SYSOP bulls. +// ISP interfaces allows SMTP Auth to be configured +// SMTP Client would fail to send any more messages if a connection failed + +// Version 1.0.0.38 + +// Don't include killed messages in L commands (except LK!) +// Implement l@ +// Add forwarding timebands +// Allow resizing of main window. +// Add Ver command. + +// Version 1.0.1.1 + +// First Public Beta + +// Fix part line handling in Console +// Maintenance deletes old log files. +// Add option to delete files to the recycle bin. + +// Version 1.0.2.1 + +// Allow all Node SYSOP commands in connect scripts. +// Implement FBB B1 Protocol with Resume +// Make FBB Max Block size settable for each BBS. +// Add extra logging when Chat Sessions refused. +// Fix Crash on invalid housekeeping override. +// Add Hold Messages option. +// Trap CRT Errors +// Sort Actions/Start Forwarding List + +// Version 1.0.2.2 + +// Fill in gaps in BBS Number sequence +// Fix PE if ctext contains } +// Run Houskeeping at startup if previous Housekeeping was missed + +// Version 1.0.2.3 + +// Add configured nodes to /p listing + +// Version 1.0.2.4 + +// Fix RMS (it wanted B2 not B12) +// Send messages if available after rejecting all proposals +// Dont try to send msg back to originator. + +// Version 1.0.2.5 + +// Fix timeband processing when none specified. +// Improved Chat Help display. +// Add helpful responses to /n /q and /t + +// Version 1.0.2.6 + +// Kill Personal WP messages after processing +// Make sure a node doesnt try to "join" or "leave" a node as a user. +// More tracing to try to track down lost topic links. +// Add command recall to Console +// Show users in new topic when changing topic +// Add Send From Clipboard" Action + +// Version 1.0.2.7 + +// Hold messages from the future, or with invalid dates. +// Add KH (kill held) command. +// Send Message to SYSOP when a new user connects. + +// Version 1.0.2.8 + +// Don't reject personal message on Dup BID unless we already have an unforwarded copy. +// Hold Looping messages. +// Warn SYSOP of held messages. + +// Version 1.0.2.9 + +// Close connecton on receipt of *** DONE (MBL style forwarding). +// Improved validation in link_drop (Chat Node) +// Change to welcome prompt and Msg Header for Outpost. +// Fix Connect Script processing for KA Nodes + +// Version 1.0.3.1 + +// Fix incorrect sending of NO - BID. +// Fix problems caused by a user being connected to more than one chat node. +// Show idle time on Chat /u display. +// Rewrite forwarding by HA. +// Add "Bad Words" Test. +// Add reason for holding to SYSOP "Message Held" Message. +// Make topics case-insensitive. +// Allow SR for smtp mail. +// Try to fix some user's "Add User" problem. + + +// Version 1.0.3.2 + +// Fix program error when prcessing - response in FBB forwarding. +// Fix code to flag messages as sent. + + +// Version 1.0.3.3 + +// Attempt to fix message loop on topic_change +// Fix loop if compressed size is greater than 32K when receiving with B1 protocol. +// Fix selection of B1 + +// Version 1.0.3.4 + +// Add "KISS ONLY" Flag to R: Lines (Needs Node Version 4.10.12 (4.10l) or above) +// Add Basic NNTP Interface +// Fix possible loop in lzhuf encode + +// Version 1.0.3.5 + +// Fix forwarding of Held Messages +// More attempts to fix Chat crashes. +// Limit join/leave problem with mismatched nodes. +// Add Chat Node Monitoring System. +// Change order of elements in nntp addresses (now to.at, was at.to) + +// Version 1.0.3.6 + +// Restart and Exit if too many errors +// Fix forwarding of killed messages. +// Fix Forwarding to PaKet. +// Fix problem if BBS signon contains words from the "Fail" list + +// Version 1.0.3.7 + +// re-fix loop if compressed size is greater than 32K - reintroduced in 1.0.3.4 +// Add last message to edit users +// Change Console and Monitor Buffer sizes +// Don't flag msg as 'Y' on read if it was Held or Killed + +// Version 1.0.3.8 + +// Don't connect if all messages for a BBS are held. +// Hold message if From or To are missing. +// Fix parsing of /n and /q commands +// fix possible loop on changing name or qth + +// Version 1.0.3.9 + +// More Chat fixes and monitoring +// Added additional console for chat + +// Version 1.0.3.10 + +// Fix for corruption of CIrcuit-Node chain. + +// Version 1.0.3.11 + +// Fix flow control for SMTP and NNTP + +// Version 1.0.3.12 + +// Fix crash in SendChatStatus if no Chat Links Defined. +// Disable Chat Mode if there is no ApplCall for ChatApplNum, +// Add Edit Message to Manage Messages Dialog +// NNTP needs authentication + + +// Version 1.0.3.13 + +// Fix Chat ApplCall warning when ChatAppl = 0 +// Add NNTP NEWGROUPS Command +// Fix MBL Forwarding (remove extra > prompt after SP) + +// Version 1.0.3.14 + +// Fix topic switch code. +// Send SYSOP messages on POP3 interface if User SYSOP flag is set. +// NNTP only needs Authentication for posting, not reading. + +// Version 1.0.3.15 + +// Fix reset of First to Forward after househeeping + +// Version 1.0.3.16 + +// Fix check of HA for terminating WW +// MBL Mode remove extra > prompts +// Fix program error if WP record has unexpected format +// Connect Script changes for WINMOR +// Fix typo in unconfigured node has connected message + +// Version 1.0.3.17 + +// Fix forwarding of Personals + +// Version 1.0.3.18 + +// Fix detection of misconfigured nodes to work with new nodes. +// Limit connection attempt rate when a chat node is unavailable. +// Fix Program Error on long input lines (> ~250 chars). + +// Version 1.0.3.19 + +// Fix Restart of B2 mode transfers. +// Fix error if other end offers B1 and you are configured for B2 only. + + +// Version 1.0.3.20 + +// Fix Paging in Chat Mode. +// Report Node Versions. + +// Version 1.0.3.21 + +// Check node is not already known when processing OK +// Add option to suppress emailing of housekeeping results + +// Version 1.0.3.22 + +// Correct Version processing when user connects via the network +// Add time controlled forwarding scripts + +// Version 1.0.3.23 + +// Changes to RMS forwarding + +// Version 1.0.3.24 + +// Fix RMS: from SMTP interface +// Accept RMS/ instead of RMS: for Thunderbird + +// Version 1.0.3.25 + +// Accept smtp: addresses from smtp client, and route to ISP gateway. +// Set FROM address of messages from RMS that are delivered to smtp client so a reply will go back via RMS. + +// Version 1.0.3.26 + +// Improve display of rms and smtp messages in message lists and message display. + +// Version 1.0.3.27 + +// Correct code that prevents mail being retured to originating BBS. +// Tidy stuck Nodes and Topics when all links close +// Fix B2 handling of @ to TO Address. + +// Version 1.0.3.28 + +// Ensure user Record for the BBS Call has BBS bit set. +// Don't send messages addressed @winlink.org if addressee is a local user with Poll RMS set. +// Add user configurable welcome messages. + +// Version 1.0.3.29 + +// Add AUTH feature to Rig Control + +// Version 1.0.3.30 + +// Process Paclink Header (;FW:) + +// Version 1.0.3.31 + +// Process Messages with attachments. +// Add inactivity timeout to Chat Console sessions. + +// Version 1.0.3.32 + +// Fix for Paclink > BBS Addresses + +// Version 1.0.3.33 + +// Fix multiple transfers per session for B2. +// Kill messages eent to paclink. +// Add option to forward messages on arrival. + +// Version 1.0.3.34 + +// Fix bbs addresses to winlink. +// Fix adding @winlink.org to imcoming paclink msgs + +// Version 1.0.3.35 + +// Fix bbs addresses to winlink. (Again) + +// Version 1.0.3.36 + +// Restart changes for RMS/paclink + +// Version 1.0.3.37 + +// Fix for RMS Express forwarding + +// Version 1.0.3.38 + +// Fixes for smtp and lower case packet addresses from Airmail +// Fix missing > afer NO - Bid in MBL mode + +// Version 1.0.3.39 + +// Use ;FW: for RMS polling. + +// Version 1.0.3.40 + +// Add ELSE Option to connect scripts. + +// Version 1.0.3.41 + +// Improved handling of Multiple Addresses +// Add user colours to chat. + +// Version 1.0.3.42 + +// Poll multiple SSID's for RMS +// Colour support for BPQTEerminal +// New /C chat command to toggle colour on or off. + +// Version 1.0.3.43 + +// Add SKIPPROMPT command to forward scripts + +// Version 1.0.4.1 + +// Non - Beta Release +// Fix possible crash/corruption with long B2 messages + +// Version 1.0.4.2 + +// Add @winlink.org to the B2 From addresss if it is just a callsign +// Route Flood Bulls on TO as well as @ + +// Version 1.0.4.3 + +// Handle Packet Addresses from RMS Express +// Fix for Housekeeping B$ messages + +// Version 1.0.4.4 + +// Remove B2 header and all but the Body part from messages forwared using MBL +// Fix handling of ;FW: from RMS Express + +// Version 1.0.4.5 + +// Disable Paging on forwarding sessions. +// Kill Msgs sent to RMS Exxpress +// Add Name to Chat *** Joined msg + +// Version 1.0.4.6 + +// Pass smtp:winlink.org messages from Airmail to local user check +// Only apply local user check to RMS: messages @winlink.org +// Check locally input smtp: messages for local winlink.org users +// Provide facility to allow only one connect on a port + +// Version 1.0.4.8 + +// Only reset last listed on L or LR commands. + +// Version 1.0.4.9 + +// Fix error in handling smtp: messages to winlink.org addresses from Airmail + +// Version 1.0.4.10 + +// Fix Badwords processing +// Add Connect Script PAUSE command + +// Version 1.0.4.11 + +// Suppress display and listing of held messages +// Add option to exclude SYSOP messages from LM, KM, etc +// Fix crash whan receiving messages with long lines via plain text forwarding + +// Version 1.0.4.12 Jul 2010 + +// Route P messages on AT +// Allow Applications above 8 + +// Version 1.0.4.13 Aug 2010 + +// Fix TidyString for addresses of form John Wiseman +// Add Try/Except around socket routines + +// Version 1.0.4.14 Aug 2010 + +// Trap "Error - TNC Not Ready" in forward script response +// Fix restart after program error +// Add INFO command +// Add SYSOP-configurable HELP Text. + +// Version 1.0.4.15 Aug 2010 + +// Semaphore Connect/Disconnect +// Semaphore RemoveTempBIDS + +// Version 1.0.4.16 Aug 2010 + +// Remove prompt after receiving unrecognised line in MBL mode. (for MSYS) + +// Version 1.0.4.17 Aug 2010 + +// Fix receiving multiple messages in FBB Uncompressed Mode +// Try to trap phantom chat node connections +// Add delay to close + + +// Version 1.0.4.18 Aug 2010 + +// Add "Send SYSTEM messages to SYSOP Call" Option +// set fwd bit on local winlink.org msgs if user is a BBS +// add winlink.org to from address of messages from WL2K that don't already have an @ + +// Version 1.0.4.19 Sept 2010 + +// Build a B2 From: address if possible, so RMS Express can reply to packet messages. +// Fix handling of addresses from WL2K with SSID's +// L@ now only matches up to length of input string. +// Remove "Type H for help" from login prompt. + +// Version 1.0.4.20 Sept 2010 + +// Process FBB 'E' response +// Handle FROM addresses with an @BBS +// Fix FROM addresses with @ on end. +// Extend delay before close after sending FQ on winmor/pactor sessions. + +// Version 1.0.4.21 Sept 2010 + +// Fix handling B2 From: with an HA +// Add "Expert User" welcome message. + +// Version 1.0.4.22 Sept 2010 + +// Version 1.0.4.23 Oct 2010 + +// Add Dup message supression +// Dont change B2 from if going to RMS + +// Version 1.0.4.24 Oct 2010 + +// Add "Save Registry Config" command +// Add forwarding on wildcarded TO for NTS +// Add option to force text mode forwarding +// Define new users as a temporaty BBS if SID received in reply to Name prompt +// Reduce delay before sending close after sending FQ on pactor sessions +// Fix processing of MIME boundary from GMail + +// Send /ex instead of ctrl/z for text mode forwarding +// Send [WL2K-BPQ... SID if user flagged as RMS Express +// Fix Chat Map reporting when more than one AXIP port +// Add Message State D for NTS Messages +// Forward messages in priority order - T, P, B +// Add Reject and Hold Filters +// Fix holding messages to local RMS users when received as part of a multiple addressee message + +// Version 1.0.4.25 Nov 2010 + +// Renumbered for release +// Add option to save Registry Config during Housekeeping + +// Version 1.0.4.26 Nov 2010 + +// Fix F> loop when doing MBL forwarding between BPQ BBSes +// Allow multiple To: addresses, separated by ; +// Allow Houskeeping Lifetime Overrides to apply to Unsent Messages. +// Set Unforwarded Bulls to status '$' +// Accept MARS and USA as continent codes for MARS Packet Addresses +// Add option to send Non-delivery notifications. + +// Version 1.0.4.27 Dec 2010 + +// Add MSGTYPES fwd file option + +// Version 1.0.4.28 Dec 2010 + +// Renumbered to for release + +// Version 1.0.4.30 Dec 2010 + +// Fix rescan requeuing where bull was rejected by a BBS +// Fiz flagging bulls received by NNTP with $ if they need to be forwarded. +// Add Chat Keepalive option. +// Fix bug in non-delivery notification. + +// Version 1.0.4.32 Jan 2011 + +// Allow "Send from Clipboard" to send to rms: or smtp: +// Allow messages received via SMTP to be bulls (TO preceeded by bull/) or NTS (to nnnnn@NTSXX or nnnnn@NTSXX.NTS) +// Fix corruption of messages converted to B2 if body contains binary data +// Fix occasional program error when forwarding B2 messages +// Limit FBB protocol data blocks to 250 to try to fix restart problem. +// Add F2 to F5 to open windows. + +// Version 1.0.4.33 Jan 2011 + +// Fix holding old bulls with forwarding info. + +// Version 1.0.4.33 Jan 2011 + +// Prevent transfer restarting after a program error. +// Allow Housekeeping to kill held messages. + +// Version 1.0.4.35 Jan 2011 + +// Add Size limits for P and T messages to MSGTYPES command +// Fix Error in MBL processing when blank lines received (introduced in .33) +// Trap possible PE in Send_MON_Datagram +// Don't use paging on chat sessions + +// Version 1.0.4.36 Jan 2011 + +// Fix error after handling first FBB block. +// Add $X and $x welcome message options. + +// Version 1.0.4.37 Jan 2011 + +// Change L command not to list the last message if no new ones are available +// Add LC I I@ IH IZ commands +// Add option to send warning to sysop if forwarded P or T message has nowhere to go +// Fixes for Winpack Compressed Download +// Fix Houskeeping when "Apply Overrides to Unsent Bulls" is set. +// Add console copy/paste. +// Add "No Bulls" Option. +// Add "Mail For" Beacon. +// Tidied up Tab order in config dialogs to help text-to-speech programs. +// Limit MaxMsgno to 99000. + +// Version 1.0.4.38 Feb 2011 + +// Renumbered for release + +// Version 1.0.4.40 April 2011 + +// Add POLLRMS command + +// Changes for Vista/Win7 (registry key change) +// Workaround for changes to RMS Express +// Fix AUTH bug in SMTP server +// Add filter to Edit Messages dialog + +// Version 1.0.4.41 April 2011 + +// Extend B2 proposals to other BPQMail systems so Reject Filter will work. +// Add Edit User Command +// Use internal Registry Save routine instead of Regedit +// Fix Start Forward/All +// Allow Winpack Compressed Upload/Download if PMS flag set (as well as BBS flag) +// Add FWD SYSOP command +// Fix security on POLLRMS command +// Add AUTH command +// Leave selection in same place after Delete User +// Combine SMTP server messages to multiple WL2K addresses into one message to WL2k +// Add option to show name as well as call on Chat messages +// Fix program error if you try to define more than 80 BBS's + +// Version 1.0.4.45 October 2011 + +// Changes to program error reporting. +// BBS "Returh to Node" command added +// Move config to "Standard" location (BPQ Directory/BPQMailChat) . +// Fix crash if "Edit Message" clicked with no message selected. + +// Version 1.0.4.46 October 2011 + +// Fix BaseDir test when BaseDir ends with \ or / +// Fix long BaseDir values (>50 chars) + +// Version 1.4.47.1 January 2012 + +// Call CloseBPQ32 on exit +// Add option to flash window instead of sounding bell on Chat Connects +// Add ShowRMS SYSOP command +// Update WP with I records from R: lines +// Send WP Updates +// Fix Paclen on Pactor-like sessions +// Fix SID and Prompt when RMS Express User is set +// Try to stop loop in Program Error/Restarting code +// Trap "UNABLE TO CONNECT" response in connect script +// Add facility to print messages or save them to a text file + +// Version 1.4.48.1 January 2012 + +// Add Send Message (as well as Send from Clipboard) +// Fix Email From: Address when forwaring using B2 +// Send WP from BBSCALL not SYSOPCALL +// Send Chat Map reports via BPQ32.dll + + +// Version 1.4.49.1 February 2012 + + +// Fix Setting Paclink mode on SNOS connects +// Remove creation of debugging file for each message +// Add Message Export and Import functions +// All printing of more than one message at a time +// Add command to toggle "Expert" status + +// Version 1.4.50.1 February 2012 + +// Fix forwarding to RMS Express users +// Route messages received via B2 to an Internet email address to RMS +// Add Reverse Poll interval +// Add full FROM address to POP3 messages +// Include HOMEBBS command in Help + + +// Version 1.4.51.1 June 2012 + +// Allow bulls to be sent from RMS Express. +// Handle BASE64 and Quoted-printable encoding of single part messages +// Work round for RMS Express "All proposals rejected" Bug. + +// Version 1.4.52.1 August 2012 + +// Fix size limit on B2 To List when sending to multiple dests +// Fix initialisation of DIRMES.SYS control record +// Allow use of Tracker and UZ7HO ports for UI messages + +// Version 1.4.53.1 September 2012 + +// Fix crash if R: line with out a CR found. + +// Version 1.4.54.1 ?? 2012 + +// Add configurable prompts +// Fix KISS-Only Test +// Send EHLO instead of HELO when Authentication is needed on SMTP session +// Add option to use local tome for bbs forwarding config +// Allow comment lines (; or @) or single space in fwd scripts +// Fix loss of forwarding info if SAVE is clicked before selecting a call + +// Version 1.4.55.1 June 2013 + +// Add option to remove users that have not connected for a long time. +// Add l@ smtp: +// Fix From: sent to POP3 Client when meaages is from RMS +// Display Email From on Manage Messages + +// Version 1.4.56.1 July 2013 + +// Add timeout +// Verify prompts +// Add IDLETIME command + + + +// Version 1.4.57.1 + +// Change default IDLETIME +// Fix display of BBS's in Web "Manage Messages" +// Add separate househeeping lifetines for T messages +// Don't change flag on forwarded or delivered messages if they sre subsequently read +// Speed up processing, mainly to stop RMS Express timing out when connecting via Telnet +// Don't append winlink.org to RMS Express or Paclink addresses if RMS is not configured +// Fix receiving NTS messages via B2 +// Add option to send "Mail For", but not FBB Headers +// Fix corruption caused with Subject longer than 60 bytes reveived from Winlink systems +// Fix Endian bug in FBB Compression code + + +// Version 1.4.58.1 + +// Change control of appending winlink.org to RMS Express or Paclink addresses to a user flag +// Lookup HomeBBS and WP for calls without a via received from RMS Express or Paclink +// Treat call@bpq as request to look up address in Home BBS/WP for messages received from RMS Express or Paclink +// Collect stats by message type +// Fix Non-Delivery notifications to SMTP messages +// Add Message Type Stats to BBS Trafic Report +// Add "Batch forward to email" +// Add EXPORT command +// Allow more BBS records +// Allow lower case connect scripts +// Fix POP3 LIST command +// Fix MIME Multipart Alternate with first part Base64 or Quoted Printable encoding +// Fix duplicates of SP SYSOP@WW Messages +// Add command line option (tidymail) to delete redundant Mail files +// Add command line option (nohomebbs) to suppress HomeBBS prompt + +// 59 April 2014 + +// Add FLARQ Mail Mode +// Fix possible crash saving restart data +// Add script command ADDLF for connect scripts over Telnet +// Add recogniton of URONODE connected message +// Add option to stop Name prompt +// Add new RMS Express users with "RMS Express User" flag set +// Validate HTML Pages +// Add NTS swap file +// Add basic File list and read functions +// Fix Traffic report + +// 60 + +// Fix security hole in readfile + +// 61 August 2014 +// Set Messages to NTS:nnnnn@NTSXX to type 'T' and remove NTS +// Dont treat "Attempting downlink" as a failure +// Add option to read messages during a list +// Fix crash during message renumber on MAC +// Timeout response to SID to try to avoid hang on an incomplete connection. +// Save config in file instead of registry +// Fix Manage Messages "EXPORT" option and check filename on EXPORT command +// Fix reverse forward prompt in MBL mode. +// Fix From address in POP3 messages where path is @winlink.org +// Fix possible program error in T message procesing +// Add MaxAge param (for incoming Bulls) + + +//62 November 2014 +// Add ZIP and Permit Bulls flag to Manage Users +// Allow users to kill their own B and anyone to kill T messages +// Improve saving of "Last Listed" +// Fix LL when paging +// Send Date received in R: Line (should fix B2 message restarts) +// Fix occasional crash in terminal part line processing +// Add "SKIPCON" forwarding command to handle nodes that include "Connected" in their CTEXT +// Fix possible retry loop when message is deferred (FBB '=' response); +// Don't remove Attachments from received bulls. + +//63 Feb 2015 + +// Fix creating Bulls from RMS Express messages. +// Fix PE if message with no To: received. +// Fix setting "RMS Express User" flag on new connects from RMS Express +// Fix deleting 'T' messages downloaded by RMS Express +// Include MPS messages in count of messages to forward. +// Add new Welcome Message variable $F for messages to forward +// Fix setting Type in B2 header when usong NTS: or BULL: +// Remove trailing spaces from BID when Creating Message from Clipboard. +// Improved handling of FBB B1/B2 Restarts. + +//64 September 2015 + +// Fix Message Type in msgs from RMS Express to Internet +// Reopen Monitor window if open when program list closed +// Only apply NTS alias file to NTS Messages +// Fix failure to store some encrypted ISP passwords +// Allow EDITUSER to change "RMS Express User" flag +// Fix reporting of Config File errors +// Fix Finding MPS Messages (First to Forward was being used incorrectly) +// Add "Save Attachment" to Web Mgmt Interface +// Support Secure Signon on Forwarding sessions to CMS +// Save Forwarding config when BBS flag on user is cleared +// Pass internally generated SYSOP messages through routing process +// Add POP3 TOP command. +// Don't set 'T' messages to 'Y' when read. +// Add optional temporary connect script on "FWD NOW" command +// Add automatic import facility +// Accept RMS mail to BBS Call even if "Poll RMS" not set. + +// 65 November 2015 + +// Fix loading Housekeeping value for forwarded bulls. +// Fix re-using Fwd script override in timer driven forwarding. +// Add ampr.org handling +// Add "Dont forward" match on TO address for NTS +// Allow listing a combinatiom of state and type, such as LNT or LPF +// Fix handling ISP messages from gmail without a '+' +// Add basic WebMail support + +// 66 + +// Autoimport messages as Dummy Call, not SYSOP Call +// Add "My Messages" display option to WebMail +// Create .csv extract of User List during hourekeeping. +// Fix processing of NTS Alising of @ Addresses +// Don't reroute Delivered NTS Messages +// Add option to stop users killing T messages +// Add multicast Receive +// Fix initialising new message database format field +// Fix "Forward Messages to BBS Call" option. +// Add Filter WP Bulls option and allow multiple WP "TO" addresses +// Fix deleting P WP messages for other stations +// Fix saving blank lines in forwarding config +// Fix paging on L@ and l< +// Fix removing DELETE from IMPORT XXX DELETE and allow multiple IMPORT lines in script +// Run DeleteRedundantMessages before renumbering messages +// Connect script now tries ELSE lines if prompt not received from remote BBS +// Send connecting call instead of BBS Name when connecting to CMS server. +// Add BID filter to Manage Messages +// Fix handling of over long suject lines in IMPORT +// Allow comments before ELSE in connect script +// Add Copy and Clear to Multicast Window +// Fix possible duplicate messages with MBL forwarding +// Set "Permit EMail" on IMPORT dummy User. +// Fix repeated running of housekeeping if clock is stepped forward. +// Fix corruption of CMS Pass field by Web interface +// Kill B2 WP bulls if FilterWPBulls set +// Include Message Type in BPQ B2 proposal extensions + +// 6.0.14.1 July 2017 + +// Fix corruption of BBSNumber if RMS Ex User and BBS both checked +// Tread B messages without an AT as Flood. +// Make sure Message headers are always saved to disk when a message status changes +// Reject message instead of failing session if TO address too long in FBB forwarding +// Fix error when FBB restart data exactly fills a packet. +// Fix possible generation of msg number zero in send nondlivery notification +// Fix problem with Web "Manage Messages" when stray message number zero appears +// Fix Crash in AMPR forward when host missing from VIA +// Fix possible addition of an spurious password entry to the ;FW: line when connecting to CMS +// Fix test for Status "D" in forward check. +// Don't cancel AUTH on SMTP RSET +// Fix "nowhere to go" message on some messages sent to smtp addresses +// Add @ from Home BBS or WP is not spcified in "Send from Clipboard" + +// 6.0.15.1 Feb 2018 + +// Fix PE if Filename missing from FILE connect script command +// Suppress reporting errors after receiving FQ +// Fix problem caused by trailing spaces on callsign in WP database +// Support mixed case WINLINK Passwords + +// 6.0.16.1 March 2018 + +// Make sure messages sent to WL2K don;'t have @ on from: address +// If message to saildocs add R: line as an X header instead of to body +// Close session if more than 4 Invalid Commmad responses sent +// Report TOP in POP3 CAPA list. Allows POP3 to work with Windows Mail client + +// 6.0.17.1 November 2018 + +// Add source routing using ! eg sp g8bpq@winlink.org!gm8bpq to send via RMS on gm8bpq +// Accept an internet email address without rms: or smtp: +// Fix "Forward messages for BBS Call" when TO isn't BBS Call +// Accept NNTP commands in either case +// Add NNTP BODY command +// Timeout POP or SMTP TCP connections that are open too long +// Add YAPP support +// Fix connect script when Node CTEXT contains "} BBS " +// Fix handling null H Route +// Detect and correct duplicate BBS Numbers +// Fix problem if BBS requests FBB blocked forwarding without compression (ie SID of F without B) +// Fix crash if YAPP entered without filenmame and send BBS prompt after YAPP error messages +// Add support for Winlink HTML Forms to WebMail interface +// Update B2 header when using NTS alias file with B2 messages + +// 6.0.18.1 January 2019 + +// Ensure callsigns in WP database are upper case. +// Various fixes for Webmail +// Fix sending direct to ampr.org addresses +// Use SYSOP Call as default for Webmail if set +// Preparations for 64 bit version + + +// 6.0.19.1 September 2019 + +// Trap missing HTML reply Template or HTML files +// Fix case problems in HTML Templates +// Fix setting To call on reply to HTML messages +// More preparations for 64 bit including saving WP info as a text file. +// Set "RMS Express User" when a new user connects using PAT +// Increace maximum length on Forwarding Alias string in Web interface +// Expand multiaddress messages from Winlink Express if "Don't add @Winlink.org" set or no RMS BBS +// Fix program error if READ used without a filename +// Trap reject messages from Winlink CMS +// Fix "delete to recycle bin" on Linux +// Handle Radio Only Messages (-T or -R suffix on calling station) +// Fix program error on saving empty Alias list on Web Forwarding page +// Add REQDIR and REQFIL +// Experimental Blocked Uncompressed forwarding +// Security fix for YAPP +// Fix WebMail Cancel Send Message +// Fix processing Hold Message response from Winlink Express + +// 6.0.20.1 April 2020 + +// Improvments to YAPP +// Add Copy forwarding config +// Add Next and Previous buttons to Webmail message read screen +// Move HTML templates from HTMLPages to inline code. +// Fix Paclen on YAPP send +// Fix bug in handling "RMS Express User" +// Fix WINPACK compressed forwarding +// Add option to send P messages to more than one BBS +// Add "Default to Don't Add WINLINK.ORG" Config option +// Re-read Badwords.sys during Housekeeping +// Add BID Hold and Reject Filters +// On SMTP Send try HELO if EHLO rejected +// Allow SID response timeout to be configured per BBS +// Fix sending bulls with PAT +// Set "Forward Messages to BBS Call" when routing Bulls on TO +// Add option to send Mail For Message to APRS +// Fix WP update +// Fix Holding messages from Webmail Interface +// Add RMR command +// Add REROUTEMSGS BBS SYSOP command +// Disable null passwords and check Exclude flag in Webmail Signin +// Add basic Webmail logging + +// 6.0.21.1 December 2020 + +// Remove nulls from displayed messages. +// Fix Holding messages from SMTP and POP3 Interfaces +// Various fixes for handling messages to/from Internet email addresses +// Fix saving Email From field in Manage Messages +// Fix sending WL2K traffic reports via TriMode. +// Fix removing successive CR from Webmail Message display +// Fix Wildcarded @ forwarding +// Fix message type when receiving NTS Msgs form Airmail +// Fix address on SERVICE messages from Winlink +// Add multiple TO processing to Webmail non-template messages +// Don't backup config file if reading it fails +// Include Port and Freq on Connected log record +// Make sure welcome mesages don't end in > +// Allow flagging unread T messages as Delivered +// Replace \ with # in forward script so commands starting with # can be sent +// Fix forwarding NTS on TO field +// Fix possible crash in text mode forwarding +// Allow decimals of days in P message lifetimes and allow Houskeeping interval to be configured +// Add DOHOUSEKEEPING sysop command +// Add MARS continent code +// Try to trap 'zombie' BBS Sessions +// On Linux if "Delete to Recycle Bin" is set move deleted messages and logs to directory Deleted under current directory. +// Fix corruption of message length when reading R2 message via Read command +// Fix paging on List command and add new combinations of List options +// Fix NNTP list and LC command when bulls are killed + +// 6.0.22.1 August 2021 + +// Fix flagging messages with attachments as read. +// Fix possible corruption of WP database and subsequent crash on reloading. +// Fix format of Web Manage Messages display +// Include SETNEXTMESSAGENUMBER in SYSOP Help Message +// Fix occasional "Incoming Connect from SWITCH" +// Fix L> with numeric dests +// Improved diagnostic for MailTCP select() error. +// Clear "RMS Express User" if user is changed to a BBS +// Fix saving Window positions on exit +// Fix parsing ReplyTemplate name in Webmail +// Handle multiple addressees for WebMail Forms messages to packet stations +// Add option to allow only known users to connect +// Add basic callsign validation to From address +// Add option to forward a user's messages to Winlink +// Move User config to main config file. +// Update message status whne reading a Forms Webmail message +// Speed up killing multiple messages +// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command + +// 6.0.23.1 June 2022 + +// Fix crash when ; added to call in send commands +// Allow smtp/ override for messages from RMS Express to send via ISP gateway +// Send Internet email from RMS Express to ISP Gateway if enabled and RMS BBS not configured +// Recompiled for Web Interface changes in Node +// Add RMS Relay SYNC Mode (.17) +// Add Protocol changes for Relay RO forwarding +// Add SendWL2KPM command to connect script to allow users other than RMS to send ;FW: string to RMS Relay +// Fix B2 Header Date in Webmail message with sttachments. +// Fix bug when using YAPP with VARA (.27) +// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command +// Add mechsnism to send bbs log records to qttermtcp. (32) +// Add MFJ forwarding Mode (No @BBS on send) +// Fix handling CR/LF split over packet boundaries +// Add Header and Footers for Webmail Send (42) +// Fix Maintenance Interval in LinBPQ (53) +// Add RMS: to valid from addresses (.56) +// Fix Web management on Android deviced (.58) +// Disconnect immediately if "Invalid Command" "*** Protocol Error" or "Already Connected" received (.70) +// Check Badword and Reject filters before processing WP Messages + +// 6.0.24.1 ?? 2022 + +// Fix ' in Webmail subject (8) +// Change web buttons to white on black when pressed (10) +// Add auto-refresh option to Webmail index page (25) +// Fix displaying help and info files with crlf line endings on Linux (28) +// Improve validation of extended FC message (32) +// Improve WP check for SYSTEM as a callsign (33) +// Improvements to RMS Relay SYNC mode (47) +// Fix BID Hold and Reject filters +// Fix Webmail auto-refresh when page exceeds 64K bytes (54) +// Fix Webmail send when using both headers/footers and attachmonts (55) +// Fix R: line corruption on some 64 bit builds +// Dont drop empty lines inm TEXTFORWARDING (61) +// Dont wait for body prompt for TEXTFORWARDING for SID [PMS-3.2-C$] (62) +// Add forwarding mode SETCALLTOSENDER for PMS Systems that don't accept < in SP (63) +// QtTerm Monitoring fixed for 63 port version of BPQ (69) + + +#include "bpqmail.h" +#include "winstdint.h" +#define MAIL +#include "Versions.h" + +#include "GetVersion.h" + +#define MAX_LOADSTRING 100 + +typedef int (WINAPI FAR *FARPROCX)(); +typedef int (WINAPI FAR *FARPROCZ)(); + +FARPROCX pDllBPQTRACE; +FARPROCZ pGetLOC; +FARPROCX pRefreshWebMailIndex; +FARPROCX pRunEventProgram; + +BOOL WINE = FALSE; + +INT_PTR CALLBACK UserEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK MsgEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK FwdEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +VOID SetupNTSAliases(char * FN); + +HKEY REGTREE = HKEY_LOCAL_MACHINE; // Default +char * REGTREETEXT = "HKEY_LOCAL_MACHINE"; + +// Global Variables: +HINSTANCE hInst; // current instance +TCHAR szTitle[MAX_LOADSTRING]; // The title bar text +TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +UINT BPQMsg; + +HWND MainWnd; +HWND hWndSess; +RECT MainRect; +HMENU hActionMenu; +static HMENU hMenu; +HMENU hDisMenu; // Disconnect Menu Handle +HMENU hFWDMenu; // Forward Menu Handle + +int SessX, SessY, SessWidth; // Params for Session Window + +char szBuff[80]; + +#define MaxSockets 64 + +int _MYTIMEZONE = 0; + +ConnectionInfo Connections[MaxSockets+1]; + +//struct SEM AllocSemaphore = {0, 0}; +//struct SEM ConSemaphore = {0, 0}; +//struct SEM OutputSEM = {0, 0}; + +//struct UserInfo ** UserRecPtr=NULL; +//int NumberofUsers=0; + +//struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes + +//struct MsgInfo ** MsgHddrPtr=NULL; +//int NumberofMessages=0; + +//int FirstMessageIndextoForward=0; // Lowest Message wirh a forward bit set - limits search + +//BIDRec ** BIDRecPtr=NULL; +//int NumberofBIDs=0; + +extern BIDRec ** TempBIDRecPtr; +//int NumberofTempBIDs=0; + +//WPRec ** WPRecPtr=NULL; +//int NumberofWPrecs=0; + +extern char ** BadWords; +//int NumberofBadWords=0; +extern char * BadFile; + +//int LatestMsg = 0; +//struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg +//int HighestBBSNumber = 0; + +//int MaxMsgno = 60000; +//int BidLifetime = 60; +//int MaintInterval = 24; +//int MaintTime = 0; +//int UserLifetime = 0; + + +BOOL cfgMinToTray; + +BOOL DisconnectOnClose; + +extern char PasswordMsg[100]; + +char cfgHOSTPROMPT[100]; + +char cfgCTEXT[100]; + +char cfgLOCALECHO[100]; + +char AttemptsMsg[]; +char disMsg[]; + +char LoginMsg[]; + +char BlankCall[]; + + +ULONG BBSApplMask; +ULONG ChatApplMask; + +int BBSApplNum; + +//int StartStream=0; +int NumberofStreams; +int MaxStreams; + +extern char BBSSID[]; +extern char ChatSID[]; + +extern char NewUserPrompt[100]; + +extern char * WelcomeMsg; +extern char * NewWelcomeMsg; +extern char * ExpertWelcomeMsg; + +extern char * Prompt; +extern char * NewPrompt; +extern char * ExpertPrompt; + +extern BOOL DontNeedHomeBBS; + +char BBSName[100]; +char MailForText[100]; + +char SignoffMsg[100]; + +char AbortedMsg[100]; + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +char NTSAliasesPath[MAX_PATH]; +extern char NTSAliasesName[MAX_PATH]; + +char BaseDir[MAX_PATH]; +char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% + +char MailDir[MAX_PATH]; + +char RlineVer[50]; + +extern BOOL KISSOnly; + +extern BOOL OpenMon; + +extern struct ALIAS ** NTSAliases; + +extern int EnableUI; +extern int RefuseBulls; +extern int SendSYStoSYSOPCall; +extern int SendBBStoSYSOPCall; +extern int DontHoldNewUsers; +extern int ForwardToMe; + +extern int MailForInterval; + +char zeros[NBMASK]; // For forward bitmask tests + +time_t MaintClock; // Time to run housekeeping + +struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List. + +// Filter Params + +char ** RejFrom; // Reject on FROM Call +char ** RejTo; // Reject on TO Call +char ** RejAt; // Reject on AT Call +char ** RejBID; // Reject on BID + +char ** HoldFrom; // Hold on FROM Call +char ** HoldTo; // Hold on TO Call +char ** HoldAt; // Hold on AT Call +char ** HoldBID; // Hold on BID + + +// Send WP Params + +BOOL SendWP; +char SendWPVIA[81]; +char SendWPTO[11]; +int SendWPType; + + +int ProgramErrors = 0; + +UCHAR BPQDirectory[260] = ""; + + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +ATOM RegisterMainWindowClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +unsigned long _beginthread( void( *start_address )(VOID * DParam), + unsigned stack_size, VOID * DParam); + +VOID SendMailForThread(VOID * Param); +BOOL CreatePipeThread(); +int DeleteRedundantMessages(); +VOID BBSSlowTimer(); +VOID CopyConfigFile(char * ConfigName); +BOOL CreateMulticastConsole(); +char * CheckToAddress(CIRCUIT * conn, char * Addr); +BOOL CheckifPacket(char * Via); +int GetHTMLForms(); + +struct _EXCEPTION_POINTERS exinfox; + +CONTEXT ContextRecord; +EXCEPTION_RECORD ExceptionRecord; + +DWORD Stack[16]; + +BOOL Restarting = FALSE; + +Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg) +{ + unsigned int SPPtr; + unsigned int SPVal; + + memcpy(&ContextRecord, exinfo->ContextRecord, sizeof(ContextRecord)); + memcpy(&ExceptionRecord, exinfo->ExceptionRecord, sizeof(ExceptionRecord)); + + SPPtr = ContextRecord.Esp; + + Debugprintf("BPQMail *** Program Error %x at %x in %s", + ExceptionRecord.ExceptionCode, ExceptionRecord.ExceptionAddress, Msg); + + + __asm{ + + mov eax, SPPtr + mov SPVal,eax + lea edi,Stack + mov esi,eax + mov ecx,64 + rep movsb + + } + + Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", + ContextRecord.Eax, ContextRecord.Ebx, ContextRecord.Ecx, + ContextRecord.Edx, ContextRecord.Esi, ContextRecord.Edi, SPVal); + + Debugprintf("Stack:"); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); + +} + + + +void myInvalidParameterHandler(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t pReserved) +{ + Logprintf(LOG_DEBUG_X, NULL, '!', "*** Error **** C Run Time Invalid Parameter Handler Called"); + + if (expression && function && file) + { + Logprintf(LOG_DEBUG_X, NULL, '!', "Expression = %S", expression); + Logprintf(LOG_DEBUG_X, NULL, '!', "Function %S", function); + Logprintf(LOG_DEBUG_X, NULL, '!', "File %S Line %d", file, line); + } +} + +// If program gets too many program errors, it will restart itself and shut down + +VOID CheckProgramErrors() +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char ProgName[256]; + + if (Restarting) + exit(0); // Make sure can't loop in restarting + + ProgramErrors++; + + if (ProgramErrors > 25) + { + Restarting = TRUE; + + Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); + + if (cfgMinToTray) + { + DeleteTrayMenuItem(MainWnd); + if (ConsHeader[0]->hConsole) + DeleteTrayMenuItem(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DeleteTrayMenuItem(ConsHeader[1]->hConsole); + if (hMonitor) + DeleteTrayMenuItem(hMonitor); + } + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + GetModuleFileName(NULL, ProgName, 256); + + Debugprintf("Attempting to Restart %s", ProgName); + + CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); + + exit(0); + } +} + + +VOID WriteMiniDump() +{ +#ifdef WIN32 + + HANDLE hFile; + BOOL ret; + char FN[256]; + + sprintf(FN, "%s/Logs/MiniDump%x.dmp", GetBPQDirectory(), time(NULL)); + + hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + // Create the minidump + + ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, MiniDumpNormal, 0, 0, 0 ); + + if(!ret) + Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); + else + Debugprintf("Minidump %s created.", FN); + CloseHandle(hFile); + } +#endif +} + + +void GetSemaphore(struct SEM * Semaphore, int ID) +{ + // + // Wait for it to be free + // +#ifdef WIN32 + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; + } +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // + // try to get semaphore + // + + _asm{ + + mov eax,1 + mov ebx, Semaphore + xchg [ebx],eax // this instruction is locked + + cmp eax,0 + jne loop1 // someone else got it - try again +; +; ok, weve got the semaphore +; + } +#else + + while (Semaphore->Flag) + usleep(10000); + + Semaphore->Flag = 1; + +#endif + return; +} + +void FreeSemaphore(struct SEM * Semaphore) +{ + Semaphore->Flag = 0; + + return; +} + +char * CmdLine; + +extern int configSaved; + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + MSG msg; + HACCEL hAccelTable; + int BPQStream, n; + struct UserInfo * user; + struct _EXCEPTION_POINTERS exinfo; + _invalid_parameter_handler oldHandler, newHandler; + char Msg[100]; + int i = 60; + struct NNTPRec * NNTPREC; + struct NNTPRec * SaveNNTPREC; + + CmdLine = _strdup(lpCmdLine); + _strlwr(CmdLine); + + if (_stricmp(lpCmdLine, "Wait") == 0) // If AutoRestart then Delay 60 Secs + { + hWnd = CreateWindow("STATIC", "Mail Restarting after Failure - Please Wait", 0, + CW_USEDEFAULT, 100, 550, 70, + NULL, NULL, hInstance, NULL); + + ShowWindow(hWnd, nCmdShow); + + while (i-- > 0) + { + sprintf(Msg, "Mail Restarting after Failure - Please Wait %d secs.", i); + SetWindowText(hWnd, Msg); + + Sleep(1000); + } + + DestroyWindow(hWnd); + } + + __try { + + // Trap CRT Errors + + newHandler = myInvalidParameterHandler; + oldHandler = _set_invalid_parameter_handler(newHandler); + + // Initialize global strings + LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadString(hInstance, IDC_BPQMailChat, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BPQMailChat)); + + // Main message loop: + + Logprintf(LOG_DEBUG_X, NULL, '!', "Program Starting"); + Logprintf(LOG_BBS, NULL, '!', "BPQMail Starting"); + Debugprintf("BPQMail Starting"); + + if (pDllBPQTRACE == 0) + Logprintf(LOG_BBS, NULL, '!', "Remote Monitor Log not available - update BPQ32.dll to enable"); + + + } My__except_Routine("Init"); + + while (GetMessage(&msg, NULL, 0, 0)) + { + __try + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + #define EXCEPTMSG "GetMessageLoop" + #include "StdExcept.c" + + CheckProgramErrors(); + } + } + + __try + { + for (n = 0; n < NumberofStreams; n++) + { + BPQStream=Connections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + + + hWnd = CreateWindow("STATIC", "Mail Closing - Please Wait", 0, + 150, 200, 350, 40, NULL, NULL, hInstance, NULL); + + ShowWindow(hWnd, nCmdShow); + + Sleep(1000); // A bit of time for links to close + + DestroyWindow(hWnd); + + if (ConsHeader[0]->hConsole) + DestroyWindow(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DestroyWindow(ConsHeader[1]->hConsole); + if (hMonitor) + { + DestroyWindow(hMonitor); + hMonitor = (HWND)1; // For status Save + } + + +// SaveUserDatabase(); + SaveMessageDatabase(); + SaveBIDDatabase(); + + configSaved = 1; + SaveConfig(ConfigName); + + if (cfgMinToTray) + { + DeleteTrayMenuItem(MainWnd); + if (ConsHeader[0]->hConsole) + DeleteTrayMenuItem(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DeleteTrayMenuItem(ConsHeader[1]->hConsole); + if (hMonitor) + DeleteTrayMenuItem(hMonitor); + } + + // Free all allocated memory + + for (n = 0; n <= NumberofUsers; n++) + { + user = UserRecPtr[n]; + + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + free(user->Temp); + + free(user); + } + + free(UserRecPtr); + + for (n = 0; n <= NumberofMessages; n++) + free(MsgHddrPtr[n]); + + free(MsgHddrPtr); + + for (n = 0; n <= NumberofWPrecs; n++) + free(WPRecPtr[n]); + + free(WPRecPtr); + + for (n = 0; n <= NumberofBIDs; n++) + free(BIDRecPtr[n]); + + free(BIDRecPtr); + + if (TempBIDRecPtr) + free(TempBIDRecPtr); + + NNTPREC = FirstNNTPRec; + + while (NNTPREC) + { + SaveNNTPREC = NNTPREC->Next; + free(NNTPREC); + NNTPREC = SaveNNTPREC; + } + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + n = 0; + + if (Aliases) + { + while(Aliases[n]) + { + free(Aliases[n]->Dest); + free(Aliases[n]); + n++; + } + + free(Aliases); + FreeList(AliasText); + } + + n = 0; + + if (NTSAliases) + { + while(NTSAliases[n]) + { + free(NTSAliases[n]->Dest); + free(NTSAliases[n]); + n++; + } + + free(NTSAliases); + } + + FreeOverrides(); + + FreeList(RejFrom); + FreeList(RejTo); + FreeList(RejAt); + FreeList(RejBID); + FreeList(HoldFrom); + FreeList(HoldTo); + FreeList(HoldAt); + FreeList(HoldBID); + FreeList(SendWPAddrs); + + Free_UI(); + + for (n=1; n<20; n++) + { + if (MyElements[n]) free(MyElements[n]); + } + + free(WelcomeMsg); + free(NewWelcomeMsg); + free(ExpertWelcomeMsg); + + free(Prompt); + free(NewPrompt); + free(ExpertPrompt); + + FreeWebMailMallocs(); + + free(CmdLine); + + _CrtDumpMemoryLeaks(); + + } + My__except_Routine("Close Processing"); + + CloseBPQ32(); // Close Ext Drivers if last bpq32 process + + return (int) msg.wParam; +} + + + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +// COMMENTS: +// +// This function and its usage are only necessary if you want this code +// to be compatible with Win32 systems prior to the 'RegisterClassEx' +// function that was added to Windows 95. It is important to call this function +// so that the application will get 'well formed' small icons associated +// with it. +// +// +#define BGCOLOUR RGB(236,233,216) +//#define BGCOLOUR RGB(245,245,245) + +HBRUSH bgBrush; + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEX wcex; + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = DLGWINDOWEXTRA; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(BPQICON)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = bgBrush; + wcex.lpszMenuName = MAKEINTRESOURCE(IDC_BPQMailChat); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(BPQICON)); + + return RegisterClassEx(&wcex); +} + + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + +HWND hWnd; + +int AXIPPort = 0; + +char LOC[7] = ""; + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + char Title[80]; + WSADATA WsaData; + HMENU hTopMenu; // handle of menu + HKEY hKey=0; + int retCode; + RECT InitRect; + RECT SessRect; + struct _EXCEPTION_POINTERS exinfo; + + HMODULE ExtDriver = LoadLibrary("bpq32.dll"); + + if (ExtDriver) + { + pDllBPQTRACE = GetProcAddress(ExtDriver,"_DllBPQTRACE@8"); + pGetLOC = GetProcAddress(ExtDriver,"_GetLOC@0"); + pRefreshWebMailIndex = GetProcAddress(ExtDriver,"_RefreshWebMailIndex@0"); + pRunEventProgram = GetProcAddress(ExtDriver,"_RunEventProgram@8"); + + if (pGetLOC) + { + char * pLOC = (char *)pGetLOC(); + memcpy(LOC, pLOC, 6); + } + } + + // See if running under WINE + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + RegCloseKey(hKey); + WINE =TRUE; + Debugprintf("Running under WINE"); + } + + + REGTREE = GetRegistryKey(); + REGTREETEXT = GetRegistryKeyText(); + + Sleep(1000); + + { + int n; + struct _EXTPORTDATA * PORTVEC; + + KISSOnly = TRUE; + + for (n=1; n <= GetNumberofPorts(); n++) + { + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(n); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + { + if (_memicmp(PORTVEC->PORT_DLL_NAME, "TELNET", 6) == 0) + KISSOnly = FALSE; + + if (PORTVEC->PORTCONTROL.PROTOCOL != 10) // Pactor/WINMOR + KISSOnly = FALSE; + + if (AXIPPort == 0) + { + if (_memicmp(PORTVEC->PORT_DLL_NAME, "BPQAXIP", 7) == 0) + { + AXIPPort = PORTVEC->PORTCONTROL.PORTNUMBER; + KISSOnly = FALSE; + } + } + } + } + } + + hInst = hInstance; + + hWnd=CreateDialog(hInst,szWindowClass,0,NULL); + + if (!hWnd) + { + return FALSE; + } + + MainWnd = hWnd; + + GetVersionInfo(NULL); + + sprintf(Title,"G8BPQ Mail Server Version %s", VersionString); + + sprintf(RlineVer, "BPQ%s%d.%d.%d", (KISSOnly) ? "K" : "", Ver[0], Ver[1], Ver[2]); + + SetWindowText(hWnd,Title); + + hWndSess = GetDlgItem(hWnd, 100); + + GetWindowRect(hWnd, &InitRect); + GetWindowRect(hWndSess, &SessRect); + + SessX = SessRect.left - InitRect.left ; + SessY = SessRect.top -InitRect.top; + SessWidth = SessRect.right - SessRect.left; + + // Get handles for updating menu items + + hTopMenu=GetMenu(MainWnd); + hActionMenu=GetSubMenu(hTopMenu,0); + + hFWDMenu=GetSubMenu(hActionMenu,0); + hMenu=GetSubMenu(hActionMenu,1); + hDisMenu=GetSubMenu(hActionMenu,2); + + CheckTimer(); + + cfgMinToTray = GetMinimizetoTrayFlag(); + + if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE)) + if (cfgMinToTray) + { + ShowWindow(hWnd, SW_HIDE); + } + else + { + ShowWindow(hWnd, nCmdShow); + } + else + ShowWindow(hWnd, nCmdShow); + + UpdateWindow(hWnd); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + __try { + + return Initialise(); + + }My__except_Routine("Initialise"); + + return FALSE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + int state,change; + ConnectionInfo * conn; + struct _EXCEPTION_POINTERS exinfo; + + + if (message == BPQMsg) + { + if (lParam & BPQMonitorAvail) + { + __try + { + DoBBSMonitorData(wParam); + } + My__except_Routine("DoMonitorData"); + + return 0; + + } + if (lParam & BPQDataAvail) + { + // Dont trap error at this level - let Node error handler pick it up +// __try +// { + DoReceivedData(wParam); +// } +// My__except_Routine("DoReceivedData") + return 0; + } + if (lParam & BPQStateChange) + { + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + __try + { + SessionState(wParam, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + __try {Connected(wParam);} + My__except_Routine("Connected"); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + __try{Disconnected(wParam);} + My__except_Routine("Disconnected"); + FreeSemaphore(&ConSemaphore); + } + } + } + My__except_Routine("DoStateChange"); + + } + + return 0; + } + + + switch (message) + { + + case WM_KEYUP: + + switch (wParam) + { + case VK_F2: + CreateConsole(-1); + return 0; + + case VK_F3: + CreateMulticastConsole(); + return 0; + + case VK_F4: + CreateMonitor(); + return 0; + + case VK_TAB: + return TRUE; + + break; + + + + } + return 0; + + case WM_TIMER: + + if (wParam == 1) // Slow = 10 secs + { + __try + { + time_t NOW = time(NULL); + struct tm * tm; + RefreshMainWindow(); + CheckTimer(); + TCPTimer(); + BBSSlowTimer(); + FWDTimerProc(); + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + CreateBBSTrafficReport(); + LastTrafficTime = NOW; + } + } + } + My__except_Routine("Slow Timer"); + } + else + __try + { + TrytoSend(); + TCPFastTimer(); + } + My__except_Routine("TrytoSend"); + + return (0); + + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)hActionMenu) + { + if (IsClipboardFormatAvailable(CF_TEXT)) + EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_ENABLED); + else + EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_GRAYED ); + + return TRUE; + } + + if (wParam == (WPARAM)hFWDMenu) + { + // Set up Forward Menu + + struct UserInfo * user; + char MenuLine[30]; + + for (user = BBSChain; user; user = user->BBSNext) + { + sprintf(MenuLine, "%s %d Msgs", user->Call, CountMessagestoForward(user)); + + if (ModifyMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, + MF_BYCOMMAND | MF_STRING, IDM_FORWARD_ALL + user->BBSNumber, MenuLine) == 0) + + AppendMenu(hFWDMenu, MF_STRING,IDM_FORWARD_ALL + user->BBSNumber, MenuLine); + } + return TRUE; + } + + if (wParam == (WPARAM)hDisMenu) + { + // Set up Disconnect Menu + + CIRCUIT * conn; + char MenuLine[30]; + int n; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + RemoveMenu(hDisMenu, IDM_DISCONNECT + n, MF_BYCOMMAND); + + if (conn->Active) + { + sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign); + AppendMenu(hDisMenu, MF_STRING, IDM_DISCONNECT + n, MenuLine); + } + } + return TRUE; + } + break; + + + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + + if (wmEvent == LBN_DBLCLK) + + break; + + if (wmId >= IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1) + { + // disconnect user + + conn=&Connections[wmId-IDM_DISCONNECT]; + + if (conn->Active) + { + Disconnect(conn->BPQStream); + } + } + + if (wmId >= IDM_FORWARD_ALL && wmId < IDM_FORWARD_ALL + 100) + { + StartForwarding(wmId - IDM_FORWARD_ALL, NULL); + return 0; + } + + switch (wmId) + { + case IDM_LOGBBS: + + ToggleParam(hMenu, hWnd, &LogBBS, IDM_LOGBBS); + break; + + case IDM_LOGCHAT: + + ToggleParam(hMenu, hWnd, &LogCHAT, IDM_LOGCHAT); + break; + + case IDM_LOGTCP: + + ToggleParam(hMenu, hWnd, &LogTCP, IDM_LOGTCP); + break; + + case IDM_HOUSEKEEPING: + + DoHouseKeeping(TRUE); + + break; + + case IDM_CONSOLE: + + CreateConsole(-1); + break; + + case IDM_MCMONITOR: + + CreateMulticastConsole(); + break; + + case IDM_MONITOR: + + CreateMonitor(); + break; + + case RESCANMSGS: + + ReRouteMessages(); + break; + + case IDM_IMPORT: + + ImportMessages(NULL, "", FALSE); + break; + + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + + case ID_HELP_ONLINEHELP: + + ShellExecute(hWnd,"open", + "http://www.cantab.net/users/john.wiseman/Documents/MailServer.html", + "", NULL, SW_SHOWNORMAL); + + break; + + case IDM_CONFIG: + DialogBox(hInst, MAKEINTRESOURCE(IDD_CONFIG), hWnd, ConfigWndProc); + break; + + case IDM_USERS: + DialogBox(hInst, MAKEINTRESOURCE(IDD_USEREDIT), hWnd, UserEditDialogProc); + break; + + case IDM_FWD: + DialogBox(hInst, MAKEINTRESOURCE(IDD_FORWARDING), hWnd, FwdEditDialogProc); + break; + + case IDM_MESSAGES: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGEDIT), hWnd, MsgEditDialogProc); + break; + + case IDM_WP: + DialogBox(hInst, MAKEINTRESOURCE(IDD_EDITWP), hWnd, WPEditDialogProc); + break; + + case ID_ACTIONS_SENDMSGFROMCLIPBOARD: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, ClpMsgDialogProc); + break; + + case ID_ACTIONS_SENDMESSAGE: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, SendMsgDialogProc); + break; + + case ID_MULTICAST: + + MulticastRX = !MulticastRX; + CheckMenuItem(hActionMenu, ID_MULTICAST, (MulticastRX) ? MF_CHECKED : MF_UNCHECKED); + break; + + case IDM_EXIT: + DestroyWindow(hWnd); + break; + + + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + case WM_SIZE: + + if (wParam == SIZE_MINIMIZED) + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + return (0); + + + case WM_SIZING: + { + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndSess, 0, 30, SessWidth, Height - 100, TRUE); + + return TRUE; + } + + + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + // TODO: Add any drawing code here... + EndPaint(hWnd, &ps); + break; + + case WM_DESTROY: + + GetWindowRect(MainWnd, &MainRect); // For save soutine + if (ConsHeader[0]->hConsole) + GetWindowRect(ConsHeader[0]->hConsole, &ConsHeader[0]->ConsoleRect); // For save soutine + if (ConsHeader[1]->hConsole) + GetWindowRect(ConsHeader[1]->hConsole, &ConsHeader[1]->ConsoleRect); // For save soutine + if (hMonitor) + GetWindowRect(hMonitor, &MonitorRect); // For save soutine + + KillTimer(hWnd,1); + KillTimer(hWnd,2); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "T"); + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 0, 0); + + return TRUE; + + case WM_SIZING: + { + HWND hWndEdit = GetDlgItem(hDlg, IDC_EDIT1); + + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndEdit, 5, 90, Width-20, Height - 140, TRUE); + + return TRUE; + } + + case WM_COMMAND: + + if (LOWORD(wParam) == IDSEND) + { + char status [3]; + struct MsgInfo * Msg; + char * via = NULL; + char BID[13]; + char FileList[32768]; + BIDRec * BIDRec; + int MsgLen; + char * MailBuffer; + char MsgFile[MAX_PATH]; + HANDLE hFile = INVALID_HANDLE_VALUE; + int WriteLen=0; + char HDest[61]; + char Destcopy[61]; + char * Vptr; + char * FileName[100]; + int FileLen[100]; + char * FileBody[100]; + int n, Files = 0; + int TotalFileSize = 0; + char * NewMsg; + + GetDlgItemText(hDlg, IDC_MSGTO, HDest, 60); + strcpy(Destcopy, HDest); + + GetDlgItemText(hDlg, IDC_MSGBID, BID, 13); + strlop(BID, ' '); + + GetDlgItemText(hDlg, IDC_ATTACHMENTS, FileList, 32767); + + // if there are attachments, check that they can be opened ane read + + n = 0; + + if (FileList[0]) + { + FILE * Handle; + struct stat STAT; + char * ptr1 = FileList, * ptr2; + + while(ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, ';'); + + if (ptr2) + *(ptr2++) = 0; + + FileName[n++] = ptr1; + + ptr1 = ptr2; + } + + FileName[n] = 0; + + // read the files + + Files = n; + n = 0; + + while (FileName[n]) + { + if (stat(FileName[n], &STAT) == -1) + { + char ErrorMessage[512]; + sprintf(ErrorMessage,"Can't find file %s", FileName[n]); + MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); + return TRUE; + } + + FileLen[n] = STAT.st_size; + + Handle = fopen(FileName[n], "rb"); + + if (Handle == NULL) + { + char ErrorMessage[512]; + sprintf(ErrorMessage,"Can't open file %s", FileName[n]); + MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); + return TRUE; + } + + FileBody[n] = malloc(FileLen[n]+1); + + fread(FileBody[n], 1, FileLen[n], Handle); + + fclose(Handle); + + TotalFileSize += FileLen[n]; + n++; + } + } + + if (strlen(HDest) == 0) + { + MessageBox(NULL, "To: Call Missing!", "BPQMail", MB_ICONERROR); + return TRUE; + } + + if (strlen(BID)) + { + if (LookupBID(BID)) + { + // Duplicate bid + + MessageBox(NULL, "Duplicate BID", "BPQMail", MB_ICONERROR); + return TRUE; + } + } + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, SYSOPCall); + + Vptr = strlop(Destcopy, '@'); + + if (Vptr == 0 && strchr(Destcopy, '!')) // Bang route without @ + { + Vptr = strchr(Destcopy, '!'); + strcpy(Msg->via, Vptr); + strlop(Destcopy, '!'); + + if (strlen(Destcopy) > 6) + memcpy(Msg->to, Destcopy, 6); + else + strcpy(Msg->to, Destcopy); + goto gotAddr; + } + + if (strlen(Destcopy) > 6) + memcpy(Msg->to, Destcopy, 6); + else + strcpy(Msg->to, Destcopy); + + _strupr(Msg->to); + + if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) + { + Vptr = HDest; + memmove(HDest, &HDest[4], strlen(HDest)); + strcpy(Msg->to, "RMS"); + + } + else if (_memicmp(HDest, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + Vptr = HDest; + memmove(HDest, &HDest[5], strlen(HDest)); + Msg->to[0] = 0; + } + } + else if (Vptr) + { + // If looks like a valid email address, treat as such + + int tolen = (Vptr - Destcopy) - 1; + + if (tolen > 6 || !CheckifPacket(Vptr)) + { + // Assume Email address + + Vptr = HDest; + + if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route + strcpy(Msg->to, "RMS"); + else if (ISP_Gateway_Enabled) + Msg->to[0] = 0; + else + { + MessageBox(NULL, "Sending to Internet Email not available", "BPQMail", MB_ICONERROR); + return TRUE; + } + } + } + if (Vptr) + { + if (strlen(Vptr) > 40) + Vptr[40] = 0; + + strcpy(Msg->via, Vptr); + } +gotAddr: + GetDlgItemText(hDlg, IDC_MSGTITLE, Msg->title, 61); + GetDlgItemText(hDlg, IDC_MSGTYPE, status, 2); + Msg->type = status[0]; + Msg->status = 'N'; + + if (strlen(BID) == 0) + sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); + + strcpy(Msg->bid, BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + MsgLen = SendDlgItemMessage(hDlg, IDC_EDIT1, WM_GETTEXTLENGTH, 0 ,0); + + MailBuffer = malloc(MsgLen + TotalFileSize + 2000); // Allow for a B2 Header if attachments + + if (Files) + { + char DateString[80]; + struct tm * tm; + + char Type[16] = "Private"; + + // Get Type + + if (Msg->type == 'B') + strcpy(Type, "Bulletin"); + else if (Msg->type == 'T') + strcpy(Type, "Traffic"); + + // Create a B2 Message + + // B2 Header + + NewMsg = MailBuffer + 1000; + + tm = gmtime((time_t *)&Msg->datecreated); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + // Remove last Source Route + + if (strchr(HDest, '!')) + { + char * bang = HDest + strlen(HDest); + + while (*(--bang) != '!'); // Find last ! + + *(bang) = 0; // remove it; + } + + NewMsg += sprintf(NewMsg, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n", + Msg->bid, DateString, Type, Msg->from, HDest, Msg->title, BBSName); + + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", MsgLen); + + for (n = 0; n < Files; n++) + { + char * p = FileName[n], * q; + + // Remove any path + + q = strchr(p, '\\'); + + while (q) + { + if (q) + *q++ = 0; + p = q; + q = strchr(p, '\\'); + } + + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + GetDlgItemText(hDlg, IDC_EDIT1, NewMsg, MsgLen+1); + NewMsg += MsgLen; + NewMsg += sprintf(NewMsg, "\r\n"); + + for (n = 0; n < Files; n++) + { + memcpy(NewMsg, FileBody[n], FileLen[n]); + NewMsg += FileLen[n]; + free(FileBody[n]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + Msg->length = NewMsg - (MailBuffer + 1000); + NewMsg = MailBuffer + 1000; + Msg->B2Flags = B2Msg | Attachments; + } + + else + { + GetDlgItemText(hDlg, IDC_EDIT1, MailBuffer, MsgLen+1); + Msg->length = MsgLen; + NewMsg = MailBuffer; + } + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = CreateFile(MsgFile, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, NewMsg, Msg->length, &WriteLen, NULL); + CloseHandle(hFile); + } + + free(MailBuffer); + + MatchMessagetoBBSList(Msg, 0); + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + EndDialog(hDlg, LOWORD(wParam)); + + return TRUE; + } + + + if (LOWORD(wParam) == IDSelectFiles) + { + char FileNames[2048]; + char FullFileNames[32768]; + OPENFILENAME Ofn; + int err; + + FileNames[0] = 0; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = hDlg; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileNames; + Ofn.nMaxFile = 2048; + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = (LPSTR)NULL; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; + Ofn.lpstrTitle = NULL;//; + + if (GetOpenFileName(&Ofn)) + { + // if one is selected, a single string is returned, if more than one, a single + // path, followed by all the strings, duuble null terminated. + + char * Names[101]; // Allow up to 100 names + int n = 0; + char * ptr = FileNames; + + while (*ptr) + { + Names[n++] = ptr; + ptr += strlen(ptr); + ptr++; + } + + GetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames, 32768); + + if (strlen(FullFileNames)) + strcat(FullFileNames, ";"); + + if (n == 1) + { + // Single Select + + strcat(FullFileNames, FileNames); + } + else + { + int i = 1; + + while(i < n) + { + strcat(FullFileNames, Names[0]); + strcat(FullFileNames, "\\"); + strcat(FullFileNames, Names[i]); + i++; + if (i < n) + strcat(FullFileNames, ";"); + } + } + SetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames); + } + else + err = GetLastError(); + return (INT_PTR)TRUE; + } + + + if (LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HGLOBAL hglb; + LPTSTR lptstr; + + switch (message) + { + case WM_INITDIALOG: + + SetWindowText(hDlg, "Send Message from Clipboard"); + + if (!IsClipboardFormatAvailable(CF_TEXT)) + break; + + if (!OpenClipboard(hDlg)) + break; + + hglb = GetClipboardData(CF_TEXT); + + if (hglb != NULL) + { + lptstr = GlobalLock(hglb); + + if (lptstr != NULL) + { + SetDlgItemText(hDlg, IDC_EDIT1, lptstr); + GlobalUnlock(hglb); + } + } + CloseClipboard(); + } + + return SendMsgDialogProc(hDlg, message, wParam, lParam); + +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +SMTPMsgs = 0; + +int RefreshMainWindow() +{ + char msg[80]; + CIRCUIT * conn; + int i,n, SYSOPMsgs = 0, HeldMsgs = 0; + time_t now; + struct tm * tm; + char tim[20]; + + SendDlgItemMessage(MainWnd,100,LB_RESETCONTENT,0,0); + + SMTPMsgs = 0; + + for (n = 0; n < NumberofStreams; n++) + { + conn=&Connections[n]; + + if (!conn->Active) + { + strcpy(msg,"Idle"); + } + else + { + { + if (conn->UserPointer == 0) + strcpy(msg,"Logging in"); + else + { + i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d", + conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream, + "BBS", conn->OutputQueueLength - conn->OutputGetPointer); + } + } + } + SendDlgItemMessage(MainWnd,100,LB_ADDSTRING,0,(LPARAM)msg); + } + + SetDlgItemInt(hWnd, IDC_MSGS, NumberofMessages, FALSE); + + n = 0; + + for (i=1; i <= NumberofMessages; i++) + { + if (MsgHddrPtr[i]->status == 'N') + { + if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0) + SYSOPMsgs++; + else + if (MsgHddrPtr[i]->to[0] == 0) + SMTPMsgs++; + } + else + { + if (MsgHddrPtr[i]->status == 'H') + HeldMsgs++; + } + } + + SetDlgItemInt(hWnd, IDC_SYSOPMSGS, SYSOPMsgs, FALSE); + SetDlgItemInt(hWnd, IDC_HELD, HeldMsgs, FALSE); + SetDlgItemInt(hWnd, IDC_SMTP, SMTPMsgs, FALSE); + + SetDlgItemInt(hWnd, IDC_MSGSEM, MsgNoSemaphore.Clashes, FALSE); + SetDlgItemInt(hWnd, IDC_ALLOCSEM, AllocSemaphore.Clashes, FALSE); + SetDlgItemInt(hWnd, IDC_CONSEM, ConSemaphore.Clashes, FALSE); + + now = time(NULL); + + tm = gmtime(&now); + sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); + SetDlgItemText(hWnd, IDC_UTC, tim); + + tm = localtime(&now); + sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); + SetDlgItemText(hWnd, IDC_LOCAL, tim); + + + return 0; +} + +#define MAX_PENDING_CONNECTS 4 + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +SOCKADDR_IN local_sin; /* Local socket - internet style */ + +PSOCKADDR_IN psin; + +SOCKET sock; + + + +BOOL Initialise() +{ + int i, len; + ConnectionInfo * conn; + struct UserInfo * user = NULL; + HKEY hKey=0; + char * ptr1; + int Attrs, ret; + char msg[500]; + TIME_ZONE_INFORMATION TimeZoneInformation; + struct stat STAT; + + GetTimeZoneInformation(&TimeZoneInformation); + + _tzset(); + _MYTIMEZONE = timezone; + _MYTIMEZONE = TimeZoneInformation.Bias * 60; + + // Register message for posting by BPQDLL + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + // See if we need to warn of possible problem with BaseDir moved by installer + + strcpy(BPQDirectory, GetBPQDirectory()); + + sprintf(BaseDir, "%s/BPQMailChat", BPQDirectory); + + len = strlen(BaseDir); + ptr1 = BaseDir; + + while (*ptr1) + { + if (*(ptr1) == '/') *(ptr1) = '\\'; + ptr1++; + } + + // Make Sure BASEDIR Exists + + Attrs = GetFileAttributes(BaseDir); + + if (Attrs == -1) + { + sprintf_s(msg, sizeof(msg), "Base Directory %s not found - should it be created?", BaseDir); + ret = MessageBox(NULL, msg, "BPQMail", MB_YESNO); + + if (ret == IDYES) + { + ret = CreateDirectory(BaseDir, NULL); + if (ret == 0) + { + MessageBox(NULL, "Failed to created Base Directory - exiting", "BPQMail", MB_ICONSTOP); + return FALSE; + } + } + else + { + MessageBox(NULL, "Can't Continue without a Base Directory - exiting", "BPQMailChat", MB_ICONSTOP); + return FALSE; + } + } + else + { + if (!(Attrs & FILE_ATTRIBUTE_DIRECTORY)) + { + sprintf_s(msg, sizeof(msg), "Base Directory %s is a file not a directory - exiting", BaseDir); + ret = MessageBox(NULL, msg, "BPQMail", MB_ICONSTOP); + + return FALSE; + } + } + + initUTF8(); + + // Set up file and directory names + + strcpy(UserDatabasePath, BaseDir); + strcat(UserDatabasePath, "\\"); + strcat(UserDatabasePath, UserDatabaseName); + + strcpy(MsgDatabasePath, BaseDir); + strcat(MsgDatabasePath, "\\"); + strcat(MsgDatabasePath, MsgDatabaseName); + + strcpy(BIDDatabasePath, BaseDir); + strcat(BIDDatabasePath, "\\"); + strcat(BIDDatabasePath, BIDDatabaseName); + + strcpy(WPDatabasePath, BaseDir); + strcat(WPDatabasePath, "\\"); + strcat(WPDatabasePath, WPDatabaseName); + + strcpy(BadWordsPath, BaseDir); + strcat(BadWordsPath, "\\"); + strcat(BadWordsPath, BadWordsName); + + strcpy(NTSAliasesPath, BaseDir); + strcat(NTSAliasesPath, "/"); + strcat(NTSAliasesPath, NTSAliasesName); + + strcpy(MailDir, BaseDir); + strcat(MailDir, "\\"); + strcat(MailDir, "Mail"); + + CreateDirectory(MailDir, NULL); // Just in case + + strcpy(ConfigName, BaseDir); + strcat(ConfigName, "\\"); + strcat(ConfigName, "BPQMail.cfg"); + + UsingingRegConfig = FALSE; + + // if config file exists use it else try to get from Registry + + if (stat(ConfigName, &STAT) == -1) + { + UsingingRegConfig = TRUE; + + if (GetConfigFromRegistry()) + { + SaveConfig(ConfigName); + } + else + { + int retCode; + + strcpy(BBSName, GetNodeCall()); + strlop(BBSName, '-'); + strlop(BBSName, ' '); + + sprintf(msg, "No configuration found - Dummy Config created"); + + retCode = MessageBox(NULL, msg, "BPQMailChat", MB_OKCANCEL); + + if (retCode == IDCANCEL) + return FALSE; + + SaveConfig(ConfigName); + } + } + + if (GetConfig(ConfigName) == EXIT_FAILURE) + { + ret = MessageBox(NULL, + "BBS Config File seems corrupt - check before continuing", "BPQMail", MB_ICONSTOP); + return FALSE; + } + + // Got a Config File + + if (MainRect.right < 100 || MainRect.bottom < 100) + { + GetWindowRect(MainWnd, &MainRect); + } + + MoveWindow(MainWnd, MainRect.left, MainRect.top, MainRect.right-MainRect.left, MainRect.bottom-MainRect.top, TRUE); + + if (OpenMon) + CreateMonitor(); + + BBSApplMask = 1<<(BBSApplNum-1); + + ShowWindow(GetDlgItem(MainWnd, 901), SW_HIDE); + ShowWindow(GetDlgItem(MainWnd, 902), SW_HIDE); + ShowWindow(GetDlgItem(MainWnd, 903), SW_HIDE); + + // Make backup copies of Databases + + CopyBIDDatabase(); + CopyMessageDatabase(); + CopyUserDatabase(); + CopyWPDatabase(); + + SetupMyHA(); + SetupFwdAliases(); + SetupNTSAliases(NTSAliasesPath); + + GetWPDatabase(); + GetMessageDatabase(); + GetUserDatabase(); + GetBIDDatabase(); + GetBadWordFile(); + GetHTMLForms(); + + UsingingRegConfig = FALSE; + + // Make sure SYSOPCALL is set + + if (SYSOPCall[0] == 0) + strcpy(SYSOPCall, BBSName); + + // Make sure there is a user record for the BBS, with BBS bit set. + + user = LookupCall(BBSName); + + if (user == NULL) + { + user = AllocateUserRecord(BBSName); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + } + + // if forwarding AMPR mail make sure User/BBS AMPR exists + + if (SendAMPRDirect) + { + BOOL NeedSave = FALSE; + + user = LookupCall("AMPR"); + + if (user == NULL) + { + user = AllocateUserRecord("AMPR"); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + NeedSave = TRUE; + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + NeedSave = TRUE; + } + + if (NeedSave) + SaveUserDatabase(); + } + + // Allocate Streams + + for (i=0; i < MaxStreams; i++) + { + conn = &Connections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofStreams++; + + BPQSetHandle(conn->BPQStream, hWnd); + + SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask | ChatApplMask); + Disconnect(conn->BPQStream); + } + + InitialiseTCP(); + + InitialiseNNTP(); + + SetupListenSet(); // Master set of listening sockets + + if (BBSApplNum) + { + SetupUIInterface(); + if (MailForInterval) + _beginthread(SendMailForThread, 0, 0); + } + + if (cfgMinToTray) + { + AddTrayMenuItem(MainWnd, "Mail Server"); + } + + SetTimer(hWnd,1,10000,NULL); // Slow Timer (10 Secs) + SetTimer(hWnd,2,100,NULL); // Send to Node and TCP Poll (100 ms) + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = _mkgmtime(tm); + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); + + if (LastHouseKeepingTime) + { + if ((now - LastHouseKeepingTime) > MaintInterval * 3600) + { + DoHouseKeeping(FALSE); + } + } + } + + if (strstr(CmdLine, "tidymail")) + DeleteRedundantMessages(); + + if (strstr(CmdLine, "nohomebbs")) + DontNeedHomeBBS = TRUE; + + if (strstr(CmdLine, "DontCheckFromCall")) + DontCheckFromCall = TRUE; + + CheckMenuItem(hMenu,IDM_LOGBBS, (LogBBS) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_LOGTCP, (LogTCP) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_LOGCHAT, (LogCHAT) ? MF_CHECKED : MF_UNCHECKED); + + RefreshMainWindow(); + +// CreateWPReport(); + + CreatePipeThread(); + + return TRUE; +} + +int ConnectState(Stream) +{ + int state; + + SessionStateNoAck(Stream, &state); + return state; +} +UCHAR * EncodeCall(UCHAR * Call) +{ + static char axcall[10]; + + ConvToAX25(Call, axcall); + return &axcall[0]; + +} + +/* +VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo) +{ + struct UserInfo * user; + + int i = FWDInfo->UserIndex; + + if (i == -1) + { + FWDInfo->UserIndex = FWDInfo->UserCall[0] = 0; // Not scanning users + } + + for (i++; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + FWDInfo->UserIndex = i; + strcpy(FWDInfo->UserCall, user->Call); + FWDInfo->FwdTimer = FWDInfo->FwdInterval - 20; + return ; + } + } + + // Finished Scan + + FWDInfo->UserIndex = FWDInfo->FwdTimer = FWDInfo->UserCall[0] = 0; +} +*/ + +#ifndef NEWROUTING + +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo) +{ +} +VOID SetupMyHA() +{ +} +VOID SetupFwdAliases() +{ +} + +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn) +{ + struct UserInfo * bbs; + struct BBSForwardingInfo * ForwardingInfo; + char ATBBS[41]; + char * HRoute; + int Count =0; + + strcpy(ATBBS, Msg->via); + HRoute = strlop(ATBBS, '.'); + + if (Msg->type == 'P') + { + // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSToList(Msg, bbs, ForwardingInfo)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSHList(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + return FALSE; + } + + // Bulls go to all matching BBSs, so the order of checking doesn't matter + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckABBS(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + Count++; + } + } + + return Count; +} +BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) +{ + char ** Calls; + char ** HRoutes; + int i, j; + + if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS + return TRUE; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + + // Check AT distributions + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + if ((HRoute) && (ForwardingInfo->Haddresses)) + { + // Match on Routes + + HRoutes = ForwardingInfo->Haddresses; + + while(HRoutes[0]) + { + i = strlen(HRoutes[0]) - 1; + j = strlen(HRoute) - 1; + + while ((i >= 0) && (j >= 0)) // Until one string rus out + { + if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards + goto next; + } + + return TRUE; + next: + HRoutes++; + } + } + + + return FALSE; + +} + +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Calls; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) +{ + char ** Calls; + + // Check AT distributions + + if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS + return TRUE; + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) +{ + char ** HRoutes; + int i, j; + + if ((HRoute) && (ForwardingInfo->Haddresses)) + { + // Match on Routes + + HRoutes = ForwardingInfo->Haddresses; + + while(HRoutes[0]) + { + i = strlen(HRoutes[0]) - 1; + j = strlen(HRoute) - 1; + + while ((i >= 0) && (j >= 0)) // Until one string rus out + { + if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards + goto next; + } + + return TRUE; + next: + HRoutes++; + } + } + return FALSE; +} + +#endif + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr; + + if (buf == NULL) return NULL; // Protect + + ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} diff --git a/BPQMail.vcxproj b/BPQMail.vcxproj index 2765bdf..2ebc492 100644 --- a/BPQMail.vcxproj +++ b/BPQMail.vcxproj @@ -1,346 +1,346 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {3766AA10-C777-4ED8-A83D-F1452DE9B665} - TelnetServer - Win32Proj - - - - Application - v141 - NotSet - true - - - Application - v141 - NotSet - true - - - Application - v141 - false - NotSet - - - Application - v141 - false - NotSet - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>15.0.28307.799 - - - C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ - C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ - true - - - true - - - C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ - C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ - false - - - false - - - - - - - Disabled - ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - true - - Level3 - EditAndContinue - CompileAsC - - - ..\Include;%(AdditionalIncludeDirectories) - - - ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) - c:\DevProgs\bpq32\BPQMail.exe - false - LIBCMT;%(IgnoreSpecificDefaultLibraries) - true - $(IntDir)$(TargetName).pdb - true - $(IntDir)BBSListings\bpqmail.map - true - Windows - MachineX86 - - - - - - - - - Disabled - ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - true - - - Level3 - ProgramDatabase - CompileAsC - - - ..\Include;%(AdditionalIncludeDirectories) - - - ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) - c:\DevProgs\bpq32\BPQMail.exe - false - LIBCMT;%(IgnoreSpecificDefaultLibraries) - true - $(IntDir)$(TargetName).pdb - true - $(IntDir)BBSListings\bpqmail.map - true - Windows - - - - - - - - - - - MaxSpeed - false - ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) - MultiThreaded - - Level3 - ProgramDatabase - CompileAsC - - - ..\Include;%(AdditionalIncludeDirectories) - - - ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) - c:\DevProgs\bpq32\BPQMail.exe - true - c:\DevProgs\bpq32\BPQMail.pdb - true - c:\DevProgs\bpq32\BPQMail.map - Windows - true - true - - MachineX86 - - - - - - - - - - - - - MaxSpeed - false - ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) - MultiThreaded - - - Level3 - ProgramDatabase - CompileAsC - - - ..\Include;%(AdditionalIncludeDirectories) - - - ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) - c:\DevProgs\bpq32\BPQMail.exe - true - c:\DevProgs\bpq32\BPQMail.pdb - true - c:\DevProgs\bpq32\BPQMail.map - Windows - true - true - - - - - - - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - All - All - $(IntDir) - $(IntDir) - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - - - - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - - - - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - All - All - $(IntDir) - $(IntDir) - $(IntDir) - $(IntDir) - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - - - - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - - - - - - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - - - - - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.obj - $(IntDir)%(Filename)1.xdc - $(IntDir)%(Filename)1.xdc - - - - - - - - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {3766AA10-C777-4ED8-A83D-F1452DE9B665} + TelnetServer + Win32Proj + + + + Application + v141 + NotSet + true + + + Application + v141 + NotSet + true + + + Application + v141 + false + NotSet + + + Application + v141 + false + NotSet + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.28307.799 + + + C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ + true + + + true + + + C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ + false + + + false + + + + + + + Disabled + ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + true + + Level3 + EditAndContinue + CompileAsC + + + ..\Include;%(AdditionalIncludeDirectories) + + + ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) + c:\DevProgs\bpq32\BPQMail.exe + false + LIBCMT;%(IgnoreSpecificDefaultLibraries) + true + $(IntDir)$(TargetName).pdb + true + $(IntDir)BBSListings\bpqmail.map + true + Windows + MachineX86 + + + + + + + + + Disabled + ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + + + Level3 + ProgramDatabase + CompileAsC + + + ..\Include;%(AdditionalIncludeDirectories) + + + ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) + c:\DevProgs\bpq32\BPQMail.exe + false + LIBCMT;%(IgnoreSpecificDefaultLibraries) + true + $(IntDir)$(TargetName).pdb + true + $(IntDir)BBSListings\bpqmail.map + true + Windows + + + + + + + + + + + MaxSpeed + false + ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + MultiThreaded + + Level3 + ProgramDatabase + CompileAsC + + + ..\Include;%(AdditionalIncludeDirectories) + + + ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) + c:\DevProgs\bpq32\BPQMail.exe + true + c:\DevProgs\bpq32\BPQMail.pdb + true + c:\DevProgs\bpq32\BPQMail.map + Windows + true + true + + MachineX86 + + + + + + + + + + + + + MaxSpeed + false + ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + CompileAsC + + + ..\Include;%(AdditionalIncludeDirectories) + + + ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) + c:\DevProgs\bpq32\BPQMail.exe + true + c:\DevProgs\bpq32\BPQMail.pdb + true + c:\DevProgs\bpq32\BPQMail.map + Windows + true + true + + + + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + All + All + $(IntDir) + $(IntDir) + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + All + All + $(IntDir) + $(IntDir) + $(IntDir) + $(IntDir) + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + + + + + + + \ No newline at end of file diff --git a/BPQMail.vcxproj.filters b/BPQMail.vcxproj.filters index 982128f..8025626 100644 --- a/BPQMail.vcxproj.filters +++ b/BPQMail.vcxproj.filters @@ -1,113 +1,113 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - - - Resource Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + + + Resource Files + + \ No newline at end of file diff --git a/BPQMail.vcxproj.user b/BPQMail.vcxproj.user index be25078..6e2aec7 100644 --- a/BPQMail.vcxproj.user +++ b/BPQMail.vcxproj.user @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/Bpq32.c b/Bpq32.c index 228dabf..19c6288 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1115,7 +1115,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Fix processing C command if first port driver is SCSPACTROR (20) // Fix crash in UZ7HO driver if bad raw frame received (21) // Fix using FLARQ chat mode with FLDIGI ddriover (22) -// Fixed to KISSHF driver (23) +// Fix to KISSHF driver (23) // Fix for application buffer loss (24) // Add Web Sockets auto-refresh option for Webmail index page (25) // Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) @@ -1173,7 +1173,10 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Detect loss of DED application (76) // Fix connects to Application Alias with UZ7HO Driver (76) // Fix Interlock of ports on same UZ7HO modem. (76) -// Add extended Ports command +// Add extended Ports command (77) +// Fix crash in Linbpq when stdout is redirected to /dev/tty? and stdin ia redirected (78) +// Fix Web Terminal (80) +// Trap ENCRYPTION message from VARA (81) #define CKernel diff --git a/CBPQ32.vcproj b/CBPQ32.vcproj index f8e9834..f1a0357 100644 --- a/CBPQ32.vcproj +++ b/CBPQ32.vcproj @@ -321,10 +321,26 @@ + + + + + + + + + + + + + + + + diff --git a/HTTPcode.c b/HTTPcode.c index 7260c8e..daeb195 100644 --- a/HTTPcode.c +++ b/HTTPcode.c @@ -1645,7 +1645,7 @@ int InnerProcessHTTPMessage(struct ConnectionInfo * conn) HostPtr = strstr(MsgPtr, "Host: "); - WebSock = strstr(MsgPtr, "Upgrade"); + WebSock = strstr(MsgPtr, "Upgrade: websocket"); if (HostPtr) { diff --git a/HTTPcode.c.bak b/HTTPcode.c.bak new file mode 100644 index 0000000..209a80f --- /dev/null +++ b/HTTPcode.c.bak @@ -0,0 +1,4849 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + +//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#define DllImport + +#include "CHeaders.h" +#include + +#include "tncinfo.h" +#include "time.h" +#include "bpq32.h" +#include "telnetserver.h" + +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include "zlib.h" + +#define CKernel +#include "httpconnectioninfo.h" + +extern int MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS, L3FRAMES; +extern int NUMBEROFNODES, MAXDESTS, L4CONNECTSOUT, L4CONNECTSIN, L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES; +extern int STATSTIME; +extern TRANSPORTENTRY * L4TABLE; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +extern BOOL APRSApplConnected; +extern char VersionString[]; +VOID FormatTime3(char * Time, time_t cTime); +DllExport int APIENTRY Get_APPLMASK(int Stream); +VOID SaveUIConfig(); +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID SetupUI(int Port); +VOID SendUIBeacon(int Port); +VOID GetParam(char * input, char * key, char * value); +VOID ARDOPAbort(struct TNCINFO * TNC); +VOID WriteMiniDump(); +BOOL KillTNC(struct TNCINFO * TNC); +BOOL RestartTNC(struct TNCINFO * TNC); +int GetAISPageInfo(char * Buffer, int ais, int adsb); +int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb); +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); +char * stristr (char *ch1, char *ch2); +int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL); +char * GetStandardPage(char * FN, int * Len); +BOOL SHA1PasswordHash(char * String, char * Hash); +char * byte_base64_encode(char *str, int len); + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; +extern char * RigWebPage; +extern COLORREF Colours[256]; + +extern BOOL IncludesMail; +extern BOOL IncludesChat; + +extern BOOL APRSWeb; +extern BOOL RigActive; + +extern HKEY REGTREE; + +extern BOOL APRSActive; + +extern UCHAR LogDirectory[]; + +extern struct RIGPORTINFO * PORTInfo[34]; +extern int NumberofPorts; + +char * strlop(char * buf, char delim); +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); +int CompareNode(const void *a, const void *b); +int CompareAlias(const void *a, const void *b); +void ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen, int InputLen); +void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen); +struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +int SetupNodeMenu(char * Buff, int SYSOP); +int StatusProc(char * Buff); +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE); + + +static struct HTTPConnectionInfo * SessionList; // active term mode sessions + +char Mycall[10]; + +char MAILPipeFileName[] = "\\\\.\\pipe\\BPQMAILWebPipe"; +char CHATPipeFileName[] = "\\\\.\\pipe\\BPQCHATWebPipe"; + +char Index[] = "%s's BPQ32 Web Server

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

Routes

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

Nodes %s

"; + +char NodeHddr[] = "
" +"" +"" +"
" +"

Nodes %s

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

Node Stats

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

Stats for Port %d

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

Beacon Configuration for Port %d

You need to be signed in to save changes

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

" +""; + + +char LinkHddr[] = "

Links

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

Sessions

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

BPQ32 Node %s Terminal Access

" +"

Please enter username and password to access the node

" +"" +"
%s%s%s
" +"" +"
User
Password
" +"

" +""; + + +char PassError[] = "

Sorry, User or Password is invalid - please try again

"; + +char BusyError[] = "

Sorry, No sessions available - please try later

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

BPQ32 Node %s

" +"
" +"

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

BPQ32 Node %s SYSOP Access

" +"

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

" +"

Please enter Callsign and Password to access the Node

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

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

BPQ32 Mail Server %s Access

" +"

Please enter Callsign and Password to access the BBS

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

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

BPQ32 Chat Server %s Access

" +"

Please enter Callsign and Password to access the Chat Server

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

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

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

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

BPQ32 Node %s

" + "

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

Ports

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

Call %s not found

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

Info for Node %s:%s

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

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

Neighbours

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

Rigcontrol

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + ""; + char RigLine[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n"; + char Tail[] = + "
RadioFreqModeSTPorts
%s%s%s/1%c%c%s
\r\n" + "\r\n"; + + ReplyLen = sprintf(_REPLYBUFFER, "%s", Page); + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigLine, RIG->WEB_Label, RIG->WEB_FREQ, RIG->WEB_MODE, RIG->WEB_SCAN, RIG->WEB_PTT, RIG->WEB_PORTS, RIG->Interlock); + } + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", Tail); + return ReplyLen; +} + + +void SendRigWebPage() +{ + int i, n; + struct ConnectionInfo * sockptr; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + + for (i = 0; i < 33; i++) + { + TNC = TNCInfo[i]; + + if (TNC && TNC->Hardware == H_TELNET) + { + TCP = TNC->TCPInfo; + + if (TCP) + { + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + if (sockptr->HTTPMode && sockptr->WebSocks && strcmp(sockptr->WebURL, "RIGCTL") == 0) + { + char RigMsg[8192]; + int RigMsgLen = strlen(RigWebPage); + char* ptr; + + RigMsg[0] = 0x81; // Fin, Data + RigMsg[1] = 126; // Unmasked, Extended Len + RigMsg[2] = RigMsgLen >> 8; + RigMsg[3] = RigMsgLen & 0xff; + strcpy(&RigMsg[4], RigWebPage); + + // If secure session enable PTT button + + if (sockptr->WebSecure) + { + while (ptr = strstr(RigMsg, "hidden")) + memcpy(ptr, " ", 6); + } + + send(sockptr->socket, RigMsg, RigMsgLen + 4, 0); + } + } + } + } + } + } +} + +// Webmail web socket code + +int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer); + +void ProcessWebmailWebSockThread(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + struct ConnectionInfo * sockptr = (struct ConnectionInfo *)conn; + char * URL = sockptr->WebURL; + int Loops = 0; + int Sent; + struct HTTPConnectionInfo Dummy = {0}; + int ReplyLen = 0; + int InputLen = 0; + +#ifdef LINBPQ + + char _REPLYBUFFER[250000]; + + ReplyLen = ProcessWebmailWebSock(URL, _REPLYBUFFER); + + // Send may block + + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { + ReplyLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + } + +#else + // Send URL to BPQMail via Pipe. Just need a dummy session, as URL contains session key + + HANDLE hPipe; + char Reply[250000]; + + + + hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + free(conn); + return; + } + + WriteFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, URL, strlen(URL), &InputLen, NULL); + + ReadFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); + + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + + // ?? do we need a thread to handle write which may block + + Sent = send(sockptr->socket, Reply, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(Reply, &Reply[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, Reply, ReplyLen, 0); + } +#endif + free(conn); + return; +} + +/* + * sha1.c + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking characters and character + * arrays assume that only 8 bits of information are stored in each + * character. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit character. + * + */ + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array within the SHA1Context provided + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of characters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single character names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} + + + + + diff --git a/KernelScript1.rc b/KernelScript1.rc index 6a77e46..55876e7 100644 --- a/KernelScript1.rc +++ b/KernelScript1.rc @@ -215,7 +215,7 @@ BEGIN MENUITEM "Strip Linefeeds", BPQStripLF MENUITEM "Log Output", BPQLogOutput MENUITEM "Send Disconnected", BPQSendDisconnected - MENUITEM "Chat Terminal Mode", CHATTERM + MENUITEM "Chat Terminal Mode (Send Keppalives)", CHATTERM MENUITEM "Restore Windows on load", ID_WINDOWS_RESTORE MENUITEM "Beep if input too long", ID_WARNWRAP MENUITEM "Wrap Input", ID_WRAP diff --git a/LinBPQ.c b/LinBPQ.c index 0ca3dc5..ba6d20f 100644 --- a/LinBPQ.c +++ b/LinBPQ.c @@ -749,7 +749,10 @@ int main(int argc, char * argv[]) // Disable Console Terminal if stdout redirected - if (!isatty(STDOUT_FILENO)) +// printf("STDOUT %d\n",isatty(STDOUT_FILENO)); +// printf("STDIN %d\n",isatty(STDIN_FILENO)); + + if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) Redirected = 1; #endif diff --git a/LinBPQ.c~ b/LinBPQ.c~ index 6cb346e..d4dddf4 100644 --- a/LinBPQ.c~ +++ b/LinBPQ.c~ @@ -1,1975 +1,1975 @@ -/* -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 -*/ - -// Control Routine for LinBPQ - -#define _CRT_SECURE_NO_DEPRECATE - -#include "CHeaders.h" -#include "bpqmail.h" -#ifdef WIN32 -#include -//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" -#else -#include -#ifndef MACBPQ -#ifndef FREEBSD -#include -#endif -#endif -#endif - -#include "time.h" - -#define Connect(stream) SessionControl(stream,1,0) -#define Disconnect(stream) SessionControl(stream,2,0) -#define ReturntoNode(stream) SessionControl(stream,3,0) -#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) - -BOOL APIENTRY Rig_Init(); - -void GetSemaphore(struct SEM * Semaphore, int ID); -void FreeSemaphore(struct SEM * Semaphore); -VOID CopyConfigFile(char * ConfigName); -VOID SendMailForThread(VOID * Param); -VOID GetUIConfig(); -Dll BOOL APIENTRY Init_IP(); -VOID OpenReportingSockets(); -VOID SetupNTSAliases(char * FN); -int DeleteRedundantMessages(); -BOOL InitializeTNCEmulator(); -VOID FindLostBuffers(); -VOID IPClose(); -DllExport BOOL APIENTRY Rig_Close(); -Dll BOOL APIENTRY Poll_IP(); -BOOL Rig_Poll(); -BOOL Rig_Poll(); -VOID CheckWL2KReportTimer(); -VOID TNCTimer(); -VOID SendLocation(); -int ChatPollStreams(); -void ChatTrytoSend(); -VOID BBSSlowTimer(); -int GetHTMLForms(); -char * AddUser(char * Call, char * password, BOOL BBSFlag); -VOID SaveChatConfigFile(char * ConfigName); -VOID SaveMH(); -int upnpClose(); -void SaveAIS(); -void initAIS(); -void DRATSPoll(); - -BOOL IncludesMail = FALSE; -BOOL IncludesChat = FALSE; - -BOOL RunMail = FALSE; -BOOL RunChat = FALSE; -BOOL needAIS= FALSE; -BOOL needADSB = FALSE; - -int CloseOnError = 0; - -VOID Poll_AGW(); -BOOL AGWAPIInit(); -int AGWAPITerminate(); - -BOOL AGWActive = FALSE; - -extern int AGWPort; - -BOOL RigActive = FALSE; - -extern ULONG ChatApplMask; -extern char Verstring[]; - -extern char SignoffMsg[]; -extern char AbortedMsg[]; -extern char InfoBoxText[]; // Text to display in Config Info Popup - -extern int LastVer[4]; // In case we need to do somthing the first time a version is run - -extern HWND MainWnd; -extern char BaseDir[]; -extern char BaseDirRaw[]; -extern char MailDir[]; -extern char WPDatabasePath[]; -extern char RlineVer[50]; - -extern BOOL LogBBS; -extern BOOL LogCHAT; -extern BOOL LogTCP; -extern BOOL ForwardToMe; - -extern int LatestMsg; -extern char BBSName[]; -extern char SYSOPCall[]; -extern char BBSSID[]; -extern char NewUserPrompt[]; - -extern int NumberofStreams; -extern int MaxStreams; -extern ULONG BBSApplMask; -extern int BBSApplNum; -extern int ChatApplNum; -extern int MaxChatStreams; - -extern int NUMBEROFTNCPORTS; - -extern int EnableUI; - -extern BOOL AUTOSAVEMH; - -extern FILE * LogHandle[4]; - -#define MaxSockets 64 - -extern ConnectionInfo Connections[MaxSockets+1]; - -time_t LastTrafficTime; -extern int MaintTime; - -#define LOG_BBS 0 -#define LOG_CHAT 1 -#define LOG_TCP 2 -#define LOG_DEBUG_X 3 - -int _MYTIMEZONE = 0; - -// flags equates - -#define F_Excluded 0x0001 -#define F_LOC 0x0002 -#define F_Expert 0x0004 -#define F_SYSOP 0x0008 -#define F_BBS 0x0010 -#define F_PAG 0x0020 -#define F_GST 0x0040 -#define F_MOD 0x0080 -#define F_PRV 0x0100 -#define F_UNP 0x0200 -#define F_NEW 0x0400 -#define F_PMS 0x0800 -#define F_EMAIL 0x1000 -#define F_HOLDMAIL 0x2000 -#define F_POLLRMS 0x4000 -#define F_SYSOP_IN_LM 0x8000 -#define F_Temp_B2_BBS 0x00010000 - -/* #define F_PWD 0x1000 */ - - -UCHAR BPQDirectory[260]; -UCHAR LogDirectory[260]; - -BOOL GetConfig(char * ConfigName); -VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); -int EncryptPass(char * Pass, char * Encrypt); -int APIENTRY FindFreeStream(); -int PollStreams(); -int APIENTRY SetAppl(int stream, int flags, int mask); -int APIENTRY SessionState(int stream, int * state, int * change); -int APIENTRY SessionControl(int stream, int command, int Mask); - -BOOL ChatInit(); -VOID CloseChat(); -VOID CloseTNCEmulator(); - -static config_t cfg; -static config_setting_t * group; - -BOOL MonBBS = TRUE; -BOOL MonCHAT = TRUE; -BOOL MonTCP = TRUE; - -BOOL LogBBS = TRUE; -BOOL LogCHAT = TRUE; -BOOL LogTCP = TRUE; - -extern BOOL LogAPRSIS; - -BOOL UIEnabled[33]; -BOOL UINull[33]; -char * UIDigi[33]; - -extern struct UserInfo ** UserRecPtr; -extern int NumberofUsers; - -extern struct UserInfo * BBSChain; // Chain of users that are BBSes - -extern struct MsgInfo ** MsgHddrPtr; -extern int NumberofMessages; - -extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search - -extern char UserDatabaseName[MAX_PATH]; -extern char UserDatabasePath[MAX_PATH]; - -extern char MsgDatabasePath[MAX_PATH]; -extern char MsgDatabaseName[MAX_PATH]; - -extern char BIDDatabasePath[MAX_PATH]; -extern char BIDDatabaseName[MAX_PATH]; - -extern char WPDatabasePath[MAX_PATH]; -extern char WPDatabaseName[MAX_PATH]; - -extern char BadWordsPath[MAX_PATH]; -extern char BadWordsName[MAX_PATH]; - -extern char NTSAliasesPath[MAX_PATH]; -extern char NTSAliasesName[MAX_PATH]; - -extern char BaseDir[MAX_PATH]; -extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% -extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat - -extern char MailDir[MAX_PATH]; - -extern time_t MaintClock; // Time to run housekeeping - -#ifdef WIN32 -BOOL KEEPGOING = 30; // 5 secs to shut down -#else -BOOL KEEPGOING = 50; // 5 secs to shut down -#endif -BOOL Restarting = FALSE; -BOOL CLOSING = FALSE; - -int ProgramErrors; -int Slowtimer = 0; - -#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer -int ReportTimer = 0; - -// Console Terminal Support - -struct ConTermS -{ - int BPQStream; - BOOL Active; - int Incoming; - - char kbbuf[INPUTLEN]; - int kbptr; - - char * KbdStack[MAXSTACK]; - int StackIndex; - - BOOL CONNECTED; - int SlowTimer; -}; - -struct ConTermS ConTerm = {0, 0}; - - -VOID CheckProgramErrors() -{ - if (Restarting) - exit(0); // Make sure can't loop in restarting - - ProgramErrors++; - - if (ProgramErrors > 25) - { - Restarting = TRUE; - - Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); - -/* - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - GetModuleFileName(NULL, ProgName, 256); - - Debugprintf("Attempting to Restart %s", ProgName); - - CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); -*/ - exit(0); - } -} - -#ifdef WIN32 - -BOOL CtrlHandler(DWORD fdwCtrlType) -{ - switch( fdwCtrlType ) - { - // Handle the CTRL-C signal. - case CTRL_C_EVENT: - printf( "Ctrl-C event\n\n" ); - CLOSING = TRUE; - Beep( 750, 300 ); - return( TRUE ); - - // CTRL-CLOSE: confirm that the user wants to exit. - case CTRL_CLOSE_EVENT: - - CLOSING = TRUE; - printf( "Ctrl-Close event\n\n" ); - Sleep(20000); - Beep( 750, 300 ); - return( TRUE ); - - // Pass other signals to the next handler. - case CTRL_BREAK_EVENT: - Beep( 900, 200 ); - printf( "Ctrl-Break event\n\n" ); - CLOSING = TRUE; - Beep( 750, 300 ); - return FALSE; - - case CTRL_LOGOFF_EVENT: - Beep( 1000, 200 ); - printf( "Ctrl-Logoff event\n\n" ); - return FALSE; - - case CTRL_SHUTDOWN_EVENT: - Beep( 750, 500 ); - printf( "Ctrl-Shutdown event\n\n" ); - CLOSING = TRUE; - Beep( 750, 300 ); - return FALSE; - - default: - return FALSE; - } -} - -#else - -// Linux Signal Handlers - -static void sigterm_handler(int sig) -{ - syslog(LOG_INFO, "terminating on SIGTERM\n"); - CLOSING = TRUE; -} - -static void sigint_handler(int sig) -{ - printf("terminating on SIGINT\n"); - CLOSING = TRUE; -} - - -static void sigusr1_handler(int sig) -{ - signal(SIGUSR1, sigusr1_handler); -} - -#endif - - -#ifndef WIN32 - -BOOL CopyFile(char * In, char * Out, BOOL Failifexists) -{ - FILE * Handle; - DWORD FileSize; - char * Buffer; - struct stat STAT; - - if (stat(In, &STAT) == -1) - return FALSE; - - FileSize = STAT.st_size; - - Handle = fopen(In, "rb"); - - if (Handle == NULL) - return FALSE; - - Buffer = malloc(FileSize+1); - - FileSize = fread(Buffer, 1, STAT.st_size, Handle); - - fclose(Handle); - - if (FileSize != STAT.st_size) - { - free(Buffer); - return FALSE; - } - - Handle = fopen(Out, "wb"); - - if (Handle == NULL) - { - free(Buffer); - return FALSE; - } - - FileSize = fwrite(Buffer, 1, STAT.st_size, Handle); - - fclose(Handle); - free(Buffer); - - return TRUE; -} -#endif - -int RefreshMainWindow() -{ - return 0; -} - -int LastSemGets = 0; - -extern int SemHeldByAPI; - -VOID MonitorThread(void * x) -{ - // Thread to detect stuck semaphore - - do - { - if ((Semaphore.Gets == LastSemGets) && Semaphore.Flag) - { - // It is stuck - try to release - - Debugprintf ("Semaphore locked - Process ID = %d, Held By %d", - Semaphore.SemProcessID, SemHeldByAPI); - - Semaphore.Flag = 0; - } - - LastSemGets = Semaphore.Gets; - - Sleep(30000); -// Debugprintf("Monitor Thread Still going %d %d %d %x %d", LastSemGets, Semaphore.Gets, Semaphore.Flag, Semaphore.SemThreadID, SemHeldByAPI); - - } - while (TRUE); -} - - - - -VOID TIMERINTERRUPT(); - -BOOL Start(); -VOID INITIALISEPORTS(); -Dll BOOL APIENTRY Init_APRS(); -VOID APRSClose(); -Dll VOID APIENTRY Poll_APRS(); -VOID HTTPTimer(); - - -#define CKernel -#include "Versions.h" - -extern struct SEM Semaphore; - -int SemHeldByAPI = 0; -BOOL IGateEnabled = TRUE; -BOOL APRSActive = FALSE; -BOOL ReconfigFlag = FALSE; -BOOL APRSReconfigFlag = FALSE; -BOOL RigReconfigFlag = FALSE; - -BOOL IPActive = FALSE; -extern BOOL IPRequired; - -extern struct WL2KInfo * WL2KReports; - -int InitDone; -char pgm[256] = "LINBPQ"; - -char SESSIONHDDR[80] = ""; -int SESSHDDRLEN = 0; - - -// Next 3 should be uninitialised so they are local to each process - -UCHAR MCOM; -UCHAR MUIONLY; -UCHAR MTX; -uint64_t MMASK; - - -UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list - -int SAVEPORT = 0; - -VOID SetApplPorts(); - -char VersionString[50] = Verstring; -char VersionStringWithBuild[50]=Verstring; -int Ver[4] = {Vers}; -char TextVerstring[50] = Verstring; - -extern UCHAR PWLen; -extern char PWTEXT[]; -extern int ISPort; - -extern char ChatConfigName[250]; - -BOOL EventsEnabled = 0; - -UCHAR * GetBPQDirectory() -{ - return BPQDirectory; -} -UCHAR * GetLogDirectory() -{ - return LogDirectory; -} -extern int POP3Timer; - -// Console Terminal Stuff - -#ifndef WIN32 - -#define _getch getchar - -/** - Linux (POSIX) implementation of _kbhit(). - Morgan McGuire, morgan@cs.brown.edu - */ - -#include -#include -#include -//#include - -int _kbhit() { - static const int STDIN = 0; - static int initialized = 0; - - if (! initialized) { - // Use termios to turn off line buffering - struct termios term; - tcgetattr(STDIN, &term); - term.c_lflag &= ~ICANON; - - tcsetattr(STDIN, TCSANOW, &term); - setbuf(stdin, NULL); - initialized = 1; - } - - int bytesWaiting; - ioctl(STDIN, FIONREAD, &bytesWaiting); - return bytesWaiting; -} - -#endif - -void ConTermInput(char * Msg) -{ - int i; - - if (ConTerm.BPQStream == 0) - { - ConTerm.BPQStream = FindFreeStream(); - - if (ConTerm.BPQStream == 255) - { - ConTerm.BPQStream = 0; - printf("No Free Streams\n"); - return; - } - } - - if (!ConTerm.CONNECTED) - SessionControl(ConTerm.BPQStream, 1, 0); - - ConTerm.StackIndex = 0; - - // Stack it - - if (ConTerm.KbdStack[19]) - free(ConTerm.KbdStack[19]); - - for (i = 18; i >= 0; i--) - { - ConTerm.KbdStack[i+1] = ConTerm.KbdStack[i]; - } - - ConTerm.KbdStack[0] = _strdup(ConTerm.kbbuf); - - ConTerm.kbbuf[ConTerm.kbptr]=13; - - SendMsg(ConTerm.BPQStream, ConTerm.kbbuf, ConTerm.kbptr+1); -} - -void ConTermPoll() -{ - int port, sesstype, paclen, maxframe, l4window, len; - int state, change, InputLen, count; - char callsign[11] = ""; - char Msg[300]; - - // Get current Session State. Any state changed is ACK'ed - // automatically. See BPQHOST functions 4 and 5. - - SessionState(ConTerm.BPQStream, &state, &change); - - if (change == 1) - { - if (state == 1) - { - // Connected - - ConTerm.CONNECTED = TRUE; - ConTerm.SlowTimer = 0; - } - else - { - ConTerm.CONNECTED = FALSE; - printf("*** Disconnected\n"); - } - } - - GetMsg(ConTerm.BPQStream, Msg, &InputLen, &count); - - if (InputLen) - { - char * ptr = Msg; - char * ptr2 = ptr; - Msg[InputLen] = 0; - - while (ptr) - { - ptr2 = strlop(ptr, 13); - - // Replace CR with CRLF - - printf(ptr); - - if (ptr2) - printf("\r\n"); - - ptr = ptr2; - } - } - - if (_kbhit()) - { - unsigned char c = _getch(); - - if (c == 0xe0) - { - // Cursor control - - c = _getch(); - - if (c == 75) // cursor left - c = 8; - } - -#ifdef WIN32 - printf("%c", c); -#endif - if (c == 8) - { - if (ConTerm.kbptr) - ConTerm.kbptr--; - printf(" \b"); // Already echoed bs - clear typed char from screen - return; - } - - if (c == 13 || c == 10) - { - ConTermInput(ConTerm.kbbuf); - ConTerm.kbptr = 0; - return; - } - - ConTerm.kbbuf[ConTerm.kbptr++] = c; - fflush(NULL); - - } - - return; - -} - - -int Redirected = 0; - -int main(int argc, char * argv[]) -{ - int i; - struct UserInfo * user = NULL; - ConnectionInfo * conn; - struct stat STAT; - PEXTPORTDATA PORTVEC; - UCHAR LogDir[260]; - -#ifdef WIN32 - - WSADATA WsaData; // receives data from WSAStartup - HWND hWnd = GetForegroundWindow(); - - WSAStartup(MAKEWORD(2, 0), &WsaData); - SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); - - // disable the [x] button. - - if (hWnd != NULL) - { - HMENU hMenu = GetSystemMenu(hWnd, 0); - if (hMenu != NULL) - { - DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); - DrawMenuBar(hWnd); - } - } - -#else - setlinebuf(stdout); - struct sigaction act; - openlog("LINBPQ", LOG_PID, LOG_DAEMON); -#ifndef MACBPQ -#ifndef FREEBSD - prctl(PR_SET_DUMPABLE, 1); // Enable Core Dumps even with setcap -#endif -#endif - - // Disable Console Terminal if stdout redirected - - if (!isatty(STDOUT_FILENO)) - Redirected = 1; - -#endif - - printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); - printf("%s\n", VerCopyright); - - if (argc > 1 && _stricmp(argv[1], "-v") == 0) - return 0; - - sprintf(RlineVer, "LinBPQ%d.%d.%d", Ver[0], Ver[1], Ver[2]); - - - Debugprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); - -#ifndef MACBPQ - _MYTIMEZONE = _timezone; -#endif - - if (_MYTIMEZONE < -86400 || _MYTIMEZONE > 86400) - _MYTIMEZONE = 0; - -#ifdef WIN32 - GetCurrentDirectory(256, BPQDirectory); - GetCurrentDirectory(256, LogDirectory); -#else - getcwd(BPQDirectory, 256); - getcwd(LogDirectory, 256); -#endif - Consoleprintf("Current Directory is %s\n", BPQDirectory); - - for (i = 1; i < argc; i++) - { - if (_memicmp(argv[i], "logdir=", 7) == 0) - { - strcpy(LogDirectory, &argv[i][7]); - break; - } - } - - - // Make sure logs directory exists - - sprintf(LogDir, "%s/logs", LogDirectory); - -#ifdef WIN32 - CreateDirectory(LogDir, NULL); -#else - mkdir(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); - chmod(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); -#endif - - if (!ProcessConfig()) - { - WritetoConsoleLocal("Configuration File Error\n"); - return (0); - } - - SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Linux (", TextVerstring); - -#ifdef MACBPQ - SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for MAC (", TextVerstring); -#endif -#ifdef FREEBSD - SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for FreeBSD (", TextVerstring); -#endif - - - GetSemaphore(&Semaphore, 0); - - if (Start() != 0) - { - FreeSemaphore(&Semaphore); - return (0); - } - - for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null - - PWLen=i; - - SetApplPorts(); - - GetUIConfig(); - - INITIALISEPORTS(); - - if (IPRequired) IPActive = Init_IP(); - - APRSActive = Init_APRS(); - - if (ISPort == 0) - IGateEnabled = 0; - - if (needAIS) - initAIS(); - - RigActive = Rig_Init(); - - FreeSemaphore(&Semaphore); - - OpenReportingSockets(); - - initUTF8(); - - InitDone = TRUE; - - Debugprintf("Monitor Thread ID %x", _beginthread(MonitorThread, 0, 0)); - - -#ifdef WIN32 -#else - openlog("LINBPQ", LOG_PID, LOG_DAEMON); - - memset (&act, '\0', sizeof(act)); - - act.sa_handler = &sigint_handler; - if (sigaction(SIGINT, &act, NULL) < 0) - perror ("SIGINT"); - - act.sa_handler = &sigterm_handler; - if (sigaction(SIGTERM, &act, NULL) < 0) - perror ("sigaction"); - - act.sa_handler = SIG_IGN; - if (sigaction(SIGHUP, &act, NULL) < 0) - perror ("SIGHUP"); - - if (sigaction(SIGPIPE, &act, NULL) < 0) - perror ("SIGPIPE"); - -#endif - - for (i = 1; i < argc; i++) - { - if (_stricmp(argv[i], "chat") == 0) - IncludesChat = TRUE; - } - - if (IncludesChat) - { - RunChat = TRUE; - - printf("Starting Chat\n"); - - sprintf (ChatConfigName, "%s/chatconfig.cfg", BPQDirectory); - printf("Config File is %s\n", ChatConfigName); - - if (stat(ChatConfigName, &STAT) == -1) - { - printf("Chat Config File not found - creating a default config\n"); - ChatApplNum = 2; - MaxChatStreams = 10; - SaveChatConfigFile(ChatConfigName); - } - - if (GetChatConfig(ChatConfigName) == EXIT_FAILURE) - { - printf("Chat Config File seems corrupt - check before continuing\n"); - return -1; - } - - if (ChatApplNum) - { - if (ChatInit() == 0) - { - printf("Chat Init Failed\n"); - RunChat = 0; - } - else - { - printf("Chat Started\n"); - } - } - else - { - printf("Chat APPLNUM not defined\n"); - RunChat = 0; - } - CopyConfigFile(ChatConfigName); - } - - // Start Mail if requested by command line or config - - for (i = 1; i < argc; i++) - { - if (_stricmp(argv[i], "mail") == 0) - IncludesMail = TRUE; - } - - - if (IncludesMail) - { - RunMail = TRUE; - - printf("Starting Mail\n"); - - sprintf (ConfigName, "%s/linmail.cfg", BPQDirectory); - printf("Config File is %s\n", ConfigName); - - if (stat(ConfigName, &STAT) == -1) - { - printf("Config File not found - creating a default config\n"); - strcpy(BBSName, MYNODECALL); - strlop(BBSName, '-'); - strlop(BBSName, ' '); - BBSApplNum = 1; - MaxStreams = 10; - SaveConfig(ConfigName); - } - - if (GetConfig(ConfigName) == EXIT_FAILURE) - { - printf("BBS Config File seems corrupt - check before continuing\n"); - return -1; - } - - printf("Config Processed\n"); - - BBSApplMask = 1<<(BBSApplNum-1); - - // See if we need to warn of possible problem with BaseDir moved by installer - - sprintf(BaseDir, "%s", BPQDirectory); - - - // Set up file and directory names - - strcpy(UserDatabasePath, BaseDir); - strcat(UserDatabasePath, "/"); - strcat(UserDatabasePath, UserDatabaseName); - - strcpy(MsgDatabasePath, BaseDir); - strcat(MsgDatabasePath, "/"); - strcat(MsgDatabasePath, MsgDatabaseName); - - strcpy(BIDDatabasePath, BaseDir); - strcat(BIDDatabasePath, "/"); - strcat(BIDDatabasePath, BIDDatabaseName); - - strcpy(WPDatabasePath, BaseDir); - strcat(WPDatabasePath, "/"); - strcat(WPDatabasePath, WPDatabaseName); - - strcpy(BadWordsPath, BaseDir); - strcat(BadWordsPath, "/"); - strcat(BadWordsPath, BadWordsName); - - strcpy(NTSAliasesPath, BaseDir); - strcat(NTSAliasesPath, "/"); - strcat(NTSAliasesPath, NTSAliasesName); - - strcpy(MailDir, BaseDir); - strcat(MailDir, "/"); - strcat(MailDir, "Mail"); - -#ifdef WIN32 - CreateDirectory(MailDir, NULL); // Just in case -#else - mkdir(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); - chmod(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); -#endif - - // Make backup copies of Databases - -// CopyConfigFile(ConfigName); - - CopyBIDDatabase(); - CopyMessageDatabase(); - CopyUserDatabase(); - CopyWPDatabase(); - - SetupMyHA(); - SetupFwdAliases(); - SetupNTSAliases(NTSAliasesPath); - - GetWPDatabase(); - - GetMessageDatabase(); - GetUserDatabase(); - GetBIDDatabase(); - GetBadWordFile(); - GetHTMLForms(); - - // Make sure there is a user record for the BBS, with BBS bit set. - - user = LookupCall(BBSName); - - if (user == NULL) - { - user = AllocateUserRecord(BBSName); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - } - - if ((user->flags & F_BBS) == 0) - { - // Not Defined as a BBS - - if(SetupNewBBS(user)) - user->flags |= F_BBS; - } - - // if forwarding AMPR mail make sure User/BBS AMPR exists - - if (SendAMPRDirect) - { - BOOL NeedSave = FALSE; - - user = LookupCall("AMPR"); - - if (user == NULL) - { - user = AllocateUserRecord("AMPR"); - user->Temp = zalloc(sizeof (struct TempUserInfo)); - NeedSave = TRUE; - } - - if ((user->flags & F_BBS) == 0) - { - // Not Defined as a BBS - - if (SetupNewBBS(user)) - user->flags |= F_BBS; - NeedSave = TRUE; - } - - if (NeedSave) - SaveUserDatabase(); - } - - - // Make sure SYSOPCALL is set - - if (SYSOPCall[0] == 0) - strcpy(SYSOPCall, BBSName); - - // See if just want to add user (mainly for setup scripts) - - if (argc == 5 && _stricmp(argv[1], "--adduser") == 0) - { - BOOL isBBS = FALSE; - char * response; - - if (_stricmp(argv[4], "TRUE") == 0) - isBBS = TRUE; - - printf("Adding User %s\r\n", argv[2]); - response = AddUser(argv[2], argv[3], isBBS); - printf("%s", response); - exit(0); - } - // Allocate Streams - - strcpy(pgm, "BBS"); - - for (i=0; i < MaxStreams; i++) - { - conn = &Connections[i]; - conn->BPQStream = FindFreeStream(); - - if (conn->BPQStream == 255) break; - - NumberofStreams++; - -// BPQSetHandle(conn->BPQStream, hWnd); - - SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask); - Disconnect(conn->BPQStream); - } - - strcpy(pgm, "LINBPQ"); - - Debugprintf("POP3 Debug Before Init TCP Timer = %d", POP3Timer); - - InitialiseTCP(); - Debugprintf("POP3 Debug Before Init NNTP Timer = %d", POP3Timer); - InitialiseNNTP(); - - SetupListenSet(); // Master set of listening sockets - - if (EnableUI || MailForInterval) - SetupUIInterface(); - - if (MailForInterval) - _beginthread(SendMailForThread, 0, 0); - - - // Calulate time to run Housekeeping - { - struct tm *tm; - time_t now; - - now = time(NULL); - - tm = gmtime(&now); - - tm->tm_hour = MaintTime / 100; - tm->tm_min = MaintTime % 100; - tm->tm_sec = 0; - - MaintClock = mktime(tm) - (time_t)_MYTIMEZONE; - - while (MaintClock < now) - MaintClock += MaintInterval * 3600; - - Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); - - if (LastHouseKeepingTime) - { - if ((now - LastHouseKeepingTime) > MaintInterval * 3600) - { - DoHouseKeeping(FALSE); - } - } - for (i = 1; i < argc; i++) - { - if (_stricmp(argv[i], "tidymail") == 0) - DeleteRedundantMessages(); - - if (_stricmp(argv[i], "nohomebbs") == 0) - DontNeedHomeBBS = TRUE; - } - - printf("Mail Started\n"); - Logprintf(LOG_BBS, NULL, '!', "Mail Starting"); - - } - } - - Debugprintf("POP3 Debug After Mail Init Timer = %d", POP3Timer); - - if (NUMBEROFTNCPORTS) - InitializeTNCEmulator(); - - AGWActive = AGWAPIInit(); - -#ifndef WIN32 - - for (i = 1; i < argc; i++) - { - if (_stricmp(argv[i], "daemon") == 0) - { - - // Convert to daemon - - pid_t pid, sid; - - /* Fork off the parent process */ - pid = fork(); - - if (pid < 0) - exit(EXIT_FAILURE); - - if (pid > 0) - exit(EXIT_SUCCESS); - - /* Change the file mode mask */ - - umask(0); - - /* Create a new SID for the child process */ - - sid = setsid(); - - if (sid < 0) - exit(EXIT_FAILURE); - - /* Change the current working directory */ - - if ((chdir("/")) < 0) - exit(EXIT_FAILURE); - - /* Close out the standard file descriptors */ - - printf("Entering daemon mode\n"); - - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); - break; - } - } -#endif - - while (KEEPGOING) - { - Sleep(100); - GetSemaphore(&Semaphore, 2); - - if (QCOUNT < 10) - { - if (CLOSING == FALSE) - FindLostBuffers(); - CLOSING = TRUE; - } - - if (CLOSING) - { - if (RunChat) - { - CloseChat(); - RunChat = FALSE; - } - - if (RunMail) - { - int BPQStream, n; - - RunMail = FALSE; - - for (n = 0; n < NumberofStreams; n++) - { - BPQStream = Connections[n].BPQStream; - - if (BPQStream) - { - SetAppl(BPQStream, 0, 0); - Disconnect(BPQStream); - DeallocateStream(BPQStream); - } - } - -// SaveUserDatabase(); - SaveMessageDatabase(); - SaveBIDDatabase(); - SaveConfig(ConfigName); - } - - KEEPGOING--; // Give time for links to close - setbuf(stdout, NULL); - printf("Closing... %d \r", KEEPGOING); - } - - - if (RigReconfigFlag) - { - RigReconfigFlag = FALSE; - Rig_Close(); - Sleep(2000); // Allow CATPTT threads to close - RigActive = Rig_Init(); - - Consoleprintf("Rigcontrol Reconfiguration Complete"); - } - - if (APRSReconfigFlag) - { - APRSReconfigFlag = FALSE; - APRSClose(); - APRSActive = Init_APRS(); - - Consoleprintf("APRS Reconfiguration Complete"); - } - - if (ReconfigFlag) - { - int i; - BPQVECSTRUC * HOSTVEC; - PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; - - ReconfigFlag = FALSE; - -// SetupBPQDirectory(); - - WritetoConsoleLocal("Reconfiguring ...\n\n"); - OutputDebugString("BPQ32 Reconfiguring ...\n"); - - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR) - { -// SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); -// SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); -// CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); - PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports - } - } - PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - IPClose(); - APRSClose(); - Rig_Close(); - CloseTNCEmulator(); - - if (AGWActive) - AGWAPITerminate(); - - WL2KReports = NULL; - -// Sleep(2000); - - Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); - Consoleprintf(VerCopyright); - - Start(); - - INITIALISEPORTS(); - - SetApplPorts(); - - GetUIConfig(); - - FreeConfig(); - - for (i=1; i<68; i++) // Include Telnet, APRS, IP Vec - { - HOSTVEC=&BPQHOSTVECTOR[i-1]; - - HOSTVEC->HOSTTRACEQ=0; - - if (HOSTVEC->HOSTSESSION !=0) - { - // Had a connection - - HOSTVEC->HOSTSESSION=0; - HOSTVEC->HOSTFLAGS |=3; // Disconnected - -// PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); - } - } - - OpenReportingSockets(); - - WritetoConsoleLocal("\n\nReconfiguration Complete\n"); - - if (IPRequired) IPActive = Init_IP(); - - APRSActive = Init_APRS(); - - if (ISPort == 0) - IGateEnabled = 0; - - RigActive = Rig_Init(); - - if (NUMBEROFTNCPORTS) - { - FreeSemaphore(&Semaphore); - InitializeTNCEmulator(); - GetSemaphore(&Semaphore, 2); - } - - FreeSemaphore(&Semaphore); - AGWActive = AGWAPIInit(); - GetSemaphore(&Semaphore, 2); - - OutputDebugString("BPQ32 Reconfiguration Complete\n"); - } - - if (IPActive) Poll_IP(); - if (RigActive) Rig_Poll(); - if (APRSActive) Poll_APRS(); - CheckWL2KReportTimer(); - - TIMERINTERRUPT(); - - FreeSemaphore(&Semaphore); - - if (Redirected == 0) - ConTermPoll(); - - if (NUMBEROFTNCPORTS) - TNCTimer(); - - if (AGWActive) - Poll_AGW(); - - DRATSPoll(); - - HTTPTimer(); - - if (ReportTimer) - { - ReportTimer--; - - if (ReportTimer == 0) - { - ReportTimer = REPORTINTERVAL; - SendLocation(); - } - } - - Slowtimer++; - - if (RunChat) - { - ChatPollStreams(); - ChatTrytoSend(); - - if (Slowtimer > 100) // 10 secs - { - ChatTimer(); - } - } - - if (RunMail) - { - PollStreams(); - - if (Slowtimer > 100) // 10 secs - { - time_t NOW = time(NULL); - struct tm * tm; - - TCPTimer(); - FWDTimerProc(); - BBSSlowTimer(); - - if (MaintClock < NOW) - { - while (MaintClock < NOW) // in case large time step - MaintClock += MaintInterval * 3600; - - Debugprintf("|Enter HouseKeeping"); - DoHouseKeeping(FALSE); - } - - tm = gmtime(&NOW); - - if (tm->tm_wday == 0) // Sunday - { - if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) - { - LastTrafficTime = NOW; - CreateBBSTrafficReport(); - } - } - } - TCPFastTimer(); - TrytoSend(); - } - - if (Slowtimer > 100) - Slowtimer = 0; - } - - printf("Closing Ports\n"); - - CloseTNCEmulator(); - - if (AGWActive) - AGWAPITerminate(); - - if (needAIS) - SaveAIS(); - - // Close Ports - - PORTVEC=(PEXTPORTDATA)PORTTABLE; - - for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External - { - if (PORTVEC->PORT_EXT_ADDR) - { - PORTVEC->PORT_EXT_ADDR(5, PORTVEC->PORTCONTROL.PORTNUMBER, NULL); - } - } - PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; - } - - if (AUTOSAVE) - SaveNodes(); - - if (AUTOSAVEMH) - SaveMH(); - - if (IPActive) - IPClose(); - - if (RunMail) - FreeWebMailMallocs(); - - upnpClose(); - - // Close any open logs - - for (i = 0; i < 4; i++) - { - if (LogHandle[i]) - fclose(LogHandle[i]); - } - - return 0; -} - -int APIENTRY WritetoConsole(char * buff) -{ - return WritetoConsoleLocal(buff); -} - -int WritetoConsoleLocal(char * buff) -{ - return printf("%s", buff); -} - -#ifdef WIN32 -void * VCOMExtInit(struct PORTCONTROL * PortEntry); -void * V4ExtInit(EXTPORTDATA * PortEntry); -#endif -//UINT SoundModemExtInit(EXTPORTDATA * PortEntry); -//UINT BaycomExtInit(EXTPORTDATA * PortEntry); - -void * AEAExtInit(struct PORTCONTROL * PortEntry); -void * MPSKExtInit(EXTPORTDATA * PortEntry); -void * HALExtInit(struct PORTCONTROL * PortEntry); - -void * AGWExtInit(struct PORTCONTROL * PortEntry); -void * KAMExtInit(struct PORTCONTROL * PortEntry); -void * WinmorExtInit(EXTPORTDATA * PortEntry); -void * SCSExtInit(struct PORTCONTROL * PortEntry); -void * TrackerExtInit(EXTPORTDATA * PortEntry); -void * TrackerMExtInit(EXTPORTDATA * PortEntry); - -void * TelnetExtInit(EXTPORTDATA * PortEntry); -void * UZ7HOExtInit(EXTPORTDATA * PortEntry); -void * FLDigiExtInit(EXTPORTDATA * PortEntry); -void * ETHERExtInit(struct PORTCONTROL * PortEntry); -void * AXIPExtInit(struct PORTCONTROL * PortEntry); -void * ARDOPExtInit(EXTPORTDATA * PortEntry); -void * VARAExtInit(EXTPORTDATA * PortEntry); -void * SerialExtInit(EXTPORTDATA * PortEntry); -void * WinRPRExtInit(EXTPORTDATA * PortEntry); -void * HSMODEMExtInit(EXTPORTDATA * PortEntry); -void * FreeDataExtInit(EXTPORTDATA * PortEntry); -void * KISSHFExtInit(EXTPORTDATA * PortEntry); - -void * InitializeExtDriver(PEXTPORTDATA PORTVEC) -{ - // Only works with built in drivers - - UCHAR Value[20]; - - strcpy(Value,PORTVEC->PORT_DLL_NAME); - - _strupr(Value); - -#ifndef FREEBSD -#ifndef MACBPQ - if (strstr(Value, "BPQETHER")) - return ETHERExtInit; -#endif -#endif - if (strstr(Value, "BPQAXIP")) - return AXIPExtInit; - - if (strstr(Value, "BPQTOAGW")) - return AGWExtInit; - - if (strstr(Value, "AEAPACTOR")) - return AEAExtInit; - - if (strstr(Value, "HALDRIVER")) - return HALExtInit; - -#ifdef WIN32 - - if (strstr(Value, "BPQVKISS")) - return VCOMExtInit; - - if (strstr(Value, "V4")) - return V4ExtInit; - -#endif -/* - if (strstr(Value, "SOUNDMODEM")) - return (UINT) SoundModemExtInit; - - if (strstr(Value, "BAYCOM")) - return (UINT) BaycomExtInit; -*/ - if (strstr(Value, "MULTIPSK")) - return MPSKExtInit; - - if (strstr(Value, "KAMPACTOR")) - return KAMExtInit; - - if (strstr(Value, "WINMOR")) - return WinmorExtInit; - - if (strstr(Value, "SCSPACTOR")) - return SCSExtInit; - - if (strstr(Value, "SCSTRACKER")) - return TrackerExtInit; - - if (strstr(Value, "TRKMULTI")) - return TrackerMExtInit; - - if (strstr(Value, "UZ7HO")) - return UZ7HOExtInit; - - if (strstr(Value, "FLDIGI")) - return FLDigiExtInit; - - if (strstr(Value, "TELNET")) - return TelnetExtInit; - - if (strstr(Value, "ARDOP")) - return ARDOPExtInit; - - if (strstr(Value, "VARA")) - return VARAExtInit; - - if (strstr(Value, "KISSHF")) - return KISSHFExtInit; - - if (strstr(Value, "SERIAL")) - return SerialExtInit; - - if (strstr(Value, "WINRPR")) - return WinRPRExtInit; - - if (strstr(Value, "HSMODEM")) - return HSMODEMExtInit; - - if (strstr(Value, "FREEDATA")) - return FreeDataExtInit; - - return(0); -} - -int APIENTRY Restart() -{ - CLOSING = TRUE; - return TRUE; -} - -int APIENTRY Reboot() -{ - // Run sudo shutdown -r -f -#ifdef WIN32 - STARTUPINFO SInfo; - PROCESS_INFORMATION PInfo; - char Cmd[] = "shutdown -r -f"; - - - SInfo.cb=sizeof(SInfo); - SInfo.lpReserved=NULL; - SInfo.lpDesktop=NULL; - SInfo.lpTitle=NULL; - SInfo.dwFlags=0; - SInfo.cbReserved2=0; - SInfo.lpReserved2=NULL; - - return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); - return 0; -#else - - char * arg_list[] = {NULL, NULL, NULL, NULL, NULL}; - pid_t child_pid; - char * Context; - signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. - - arg_list[0] = "sudo"; - arg_list[1] = "shutdown"; - arg_list[2] = "now"; - arg_list[3] = "-r"; - - // Fork and Exec shutdown - - // Duplicate this process. - - child_pid = fork(); - - if (child_pid == -1) - { - printf ("Reboot fork() Failed\n"); - return 0; - } - - if (child_pid == 0) - { - execvp (arg_list[0], arg_list); - - /* The execvp function returns only if an error occurs. */ - - printf ("Failed to run shutdown\n"); - exit(0); // Kill the new process - } - return TRUE; -#endif - -} - -int APIENTRY Reconfig() -{ - if (!ProcessConfig()) - { - return (0); - } - SaveNodes(); - WritetoConsoleLocal("Nodes Saved\n"); - ReconfigFlag=TRUE; - WritetoConsoleLocal("Reconfig requested ... Waiting for Timer Poll\n"); - return 1; -} - -int APRSWriteLog(char * msg); - -VOID MonitorAPRSIS(char * Msg, size_t MsgLen, BOOL TX) -{ - char Line[300]; - char Copy[300]; - int Len; - struct tm * TM; - time_t NOW; - - if (LogAPRSIS == 0) - return; - - if (MsgLen > 250) - return; - - // Mustn't change Msg - - memcpy(Copy, Msg, MsgLen); - Copy[MsgLen] = 0; - - NOW = time(NULL); - TM = gmtime(&NOW); - - Len = sprintf_s(Line, 299, "%02d:%02d:%02d%c %s", TM->tm_hour, TM->tm_min, TM->tm_sec, (TX)? 'T': 'R', Copy); - - APRSWriteLog(Line); - -} - -struct TNCINFO * TNC; - -#ifndef WIN32 - -#include -#include - -#ifndef MACBPQ -#ifdef __MACH__ - -#include - -#define CLOCK_REALTIME 0 -#define CLOCK_MONOTONIC 0 - -int clock_gettime(int clk_id, struct timespec *t){ - mach_timebase_info_data_t timebase; - mach_timebase_info(&timebase); - uint64_t time; - time = mach_absolute_time(); - double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom); - double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9); - t->tv_sec = seconds; - t->tv_nsec = nseconds; - return 0; -} -#endif -#endif - -int GetTickCount() -{ - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); -} - - - -void SetWindowText(HWND hWnd, char * lpString) -{ - return; -}; - -BOOL SetDlgItemText(HWND hWnd, int item, char * lpString) -{ - return 0; -}; - -#endif - -int GetListeningPortsPID(int Port) -{ -#ifdef WIN32 - - MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; - PMIB_TCPROW_OWNER_PID Row; - int dwSize = 0; - unsigned int n; - - // Get PID of process for this TCP Port - - // Get Length of table - - GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); - - TcpTable = malloc(dwSize); - GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); - - for (n = 0; n < TcpTable->dwNumEntries; n++) - { - Row = &TcpTable->table[n]; - - if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) - { - return Row->dwOwningPid; - break; - } - } -#endif - return 0; // Not found -} - - - -VOID Check_Timer() -{ -} - -VOID POSTDATAAVAIL(){}; - -COLORREF Colours[256] = {0, - RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 - RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 - RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 - RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 - RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 - - RGB(6425,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 - RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), - RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), - RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), - RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), - - RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 - RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), - RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), - RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), - RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), - - RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 - RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), - RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), - RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), - RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,255,255), - - RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 - RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), - RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), - RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), - RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,255,255) -}; - - -//VOID SendRPBeacon(struct TNCINFO * TNC) -//{ -//} - -int PollStreams() -{ - int state,change; - ConnectionInfo * conn; - int n; - struct UserInfo * user = NULL; - char ConnectedMsg[] = "*** CONNECTED "; - - for (n = 0; n < NumberofStreams; n++) - { - conn = &Connections[n]; - - DoReceivedData(conn->BPQStream); - DoBBSMonitorData(conn->BPQStream); - - SessionState(conn->BPQStream, &state, &change); - - if (change == 1) - { - if (state == 1) // Connected - { - GetSemaphore(&ConSemaphore, 0); - Connected(conn->BPQStream); - FreeSemaphore(&ConSemaphore); - } - else - { - GetSemaphore(&ConSemaphore, 0); - Disconnected(conn->BPQStream); - FreeSemaphore(&ConSemaphore); - } - } - } - - return 0; -} - - -VOID CloseConsole(int Stream) -{ -} - -#ifndef WIN32 - -int V4ProcessReceivedData(struct TNCINFO * TNC) -{ - return 0; -} -#endif - -#ifdef FREEBSD - -char * gcvt(double _Val, int _NumOfDigits, char * _DstBuf) -{ - sprintf(_DstBuf, "%f", _Val); - return _DstBuf; -} - -#endif - - - - - +/* +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 +*/ + +// Control Routine for LinBPQ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "CHeaders.h" +#include "bpqmail.h" +#ifdef WIN32 +#include +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#ifndef MACBPQ +#ifndef FREEBSD +#include +#endif +#endif +#endif + +#include "time.h" + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +BOOL APIENTRY Rig_Init(); + +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); +VOID CopyConfigFile(char * ConfigName); +VOID SendMailForThread(VOID * Param); +VOID GetUIConfig(); +Dll BOOL APIENTRY Init_IP(); +VOID OpenReportingSockets(); +VOID SetupNTSAliases(char * FN); +int DeleteRedundantMessages(); +BOOL InitializeTNCEmulator(); +VOID FindLostBuffers(); +VOID IPClose(); +DllExport BOOL APIENTRY Rig_Close(); +Dll BOOL APIENTRY Poll_IP(); +BOOL Rig_Poll(); +BOOL Rig_Poll(); +VOID CheckWL2KReportTimer(); +VOID TNCTimer(); +VOID SendLocation(); +int ChatPollStreams(); +void ChatTrytoSend(); +VOID BBSSlowTimer(); +int GetHTMLForms(); +char * AddUser(char * Call, char * password, BOOL BBSFlag); +VOID SaveChatConfigFile(char * ConfigName); +VOID SaveMH(); +int upnpClose(); +void SaveAIS(); +void initAIS(); +void DRATSPoll(); + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; + +BOOL RunMail = FALSE; +BOOL RunChat = FALSE; +BOOL needAIS= FALSE; +BOOL needADSB = FALSE; + +int CloseOnError = 0; + +VOID Poll_AGW(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +BOOL AGWActive = FALSE; + +extern int AGWPort; + +BOOL RigActive = FALSE; + +extern ULONG ChatApplMask; +extern char Verstring[]; + +extern char SignoffMsg[]; +extern char AbortedMsg[]; +extern char InfoBoxText[]; // Text to display in Config Info Popup + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +extern HWND MainWnd; +extern char BaseDir[]; +extern char BaseDirRaw[]; +extern char MailDir[]; +extern char WPDatabasePath[]; +extern char RlineVer[50]; + +extern BOOL LogBBS; +extern BOOL LogCHAT; +extern BOOL LogTCP; +extern BOOL ForwardToMe; + +extern int LatestMsg; +extern char BBSName[]; +extern char SYSOPCall[]; +extern char BBSSID[]; +extern char NewUserPrompt[]; + +extern int NumberofStreams; +extern int MaxStreams; +extern ULONG BBSApplMask; +extern int BBSApplNum; +extern int ChatApplNum; +extern int MaxChatStreams; + +extern int NUMBEROFTNCPORTS; + +extern int EnableUI; + +extern BOOL AUTOSAVEMH; + +extern FILE * LogHandle[4]; + +#define MaxSockets 64 + +extern ConnectionInfo Connections[MaxSockets+1]; + +time_t LastTrafficTime; +extern int MaintTime; + +#define LOG_BBS 0 +#define LOG_CHAT 1 +#define LOG_TCP 2 +#define LOG_DEBUG_X 3 + +int _MYTIMEZONE = 0; + +// flags equates + +#define F_Excluded 0x0001 +#define F_LOC 0x0002 +#define F_Expert 0x0004 +#define F_SYSOP 0x0008 +#define F_BBS 0x0010 +#define F_PAG 0x0020 +#define F_GST 0x0040 +#define F_MOD 0x0080 +#define F_PRV 0x0100 +#define F_UNP 0x0200 +#define F_NEW 0x0400 +#define F_PMS 0x0800 +#define F_EMAIL 0x1000 +#define F_HOLDMAIL 0x2000 +#define F_POLLRMS 0x4000 +#define F_SYSOP_IN_LM 0x8000 +#define F_Temp_B2_BBS 0x00010000 + +/* #define F_PWD 0x1000 */ + + +UCHAR BPQDirectory[260]; +UCHAR LogDirectory[260]; + +BOOL GetConfig(char * ConfigName); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +int EncryptPass(char * Pass, char * Encrypt); +int APIENTRY FindFreeStream(); +int PollStreams(); +int APIENTRY SetAppl(int stream, int flags, int mask); +int APIENTRY SessionState(int stream, int * state, int * change); +int APIENTRY SessionControl(int stream, int command, int Mask); + +BOOL ChatInit(); +VOID CloseChat(); +VOID CloseTNCEmulator(); + +static config_t cfg; +static config_setting_t * group; + +BOOL MonBBS = TRUE; +BOOL MonCHAT = TRUE; +BOOL MonTCP = TRUE; + +BOOL LogBBS = TRUE; +BOOL LogCHAT = TRUE; +BOOL LogTCP = TRUE; + +extern BOOL LogAPRSIS; + +BOOL UIEnabled[33]; +BOOL UINull[33]; +char * UIDigi[33]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char NTSAliasesPath[MAX_PATH]; +extern char NTSAliasesName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + +extern char MailDir[MAX_PATH]; + +extern time_t MaintClock; // Time to run housekeeping + +#ifdef WIN32 +BOOL KEEPGOING = 30; // 5 secs to shut down +#else +BOOL KEEPGOING = 50; // 5 secs to shut down +#endif +BOOL Restarting = FALSE; +BOOL CLOSING = FALSE; + +int ProgramErrors; +int Slowtimer = 0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +// Console Terminal Support + +struct ConTermS +{ + int BPQStream; + BOOL Active; + int Incoming; + + char kbbuf[INPUTLEN]; + int kbptr; + + char * KbdStack[MAXSTACK]; + int StackIndex; + + BOOL CONNECTED; + int SlowTimer; +}; + +struct ConTermS ConTerm = {0, 0}; + + +VOID CheckProgramErrors() +{ + if (Restarting) + exit(0); // Make sure can't loop in restarting + + ProgramErrors++; + + if (ProgramErrors > 25) + { + Restarting = TRUE; + + Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); + +/* + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + GetModuleFileName(NULL, ProgName, 256); + + Debugprintf("Attempting to Restart %s", ProgName); + + CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); +*/ + exit(0); + } +} + +#ifdef WIN32 + +BOOL CtrlHandler(DWORD fdwCtrlType) +{ + switch( fdwCtrlType ) + { + // Handle the CTRL-C signal. + case CTRL_C_EVENT: + printf( "Ctrl-C event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return( TRUE ); + + // CTRL-CLOSE: confirm that the user wants to exit. + case CTRL_CLOSE_EVENT: + + CLOSING = TRUE; + printf( "Ctrl-Close event\n\n" ); + Sleep(20000); + Beep( 750, 300 ); + return( TRUE ); + + // Pass other signals to the next handler. + case CTRL_BREAK_EVENT: + Beep( 900, 200 ); + printf( "Ctrl-Break event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return FALSE; + + case CTRL_LOGOFF_EVENT: + Beep( 1000, 200 ); + printf( "Ctrl-Logoff event\n\n" ); + return FALSE; + + case CTRL_SHUTDOWN_EVENT: + Beep( 750, 500 ); + printf( "Ctrl-Shutdown event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return FALSE; + + default: + return FALSE; + } +} + +#else + +// Linux Signal Handlers + +static void sigterm_handler(int sig) +{ + syslog(LOG_INFO, "terminating on SIGTERM\n"); + CLOSING = TRUE; +} + +static void sigint_handler(int sig) +{ + printf("terminating on SIGINT\n"); + CLOSING = TRUE; +} + + +static void sigusr1_handler(int sig) +{ + signal(SIGUSR1, sigusr1_handler); +} + +#endif + + +#ifndef WIN32 + +BOOL CopyFile(char * In, char * Out, BOOL Failifexists) +{ + FILE * Handle; + DWORD FileSize; + char * Buffer; + struct stat STAT; + + if (stat(In, &STAT) == -1) + return FALSE; + + FileSize = STAT.st_size; + + Handle = fopen(In, "rb"); + + if (Handle == NULL) + return FALSE; + + Buffer = malloc(FileSize+1); + + FileSize = fread(Buffer, 1, STAT.st_size, Handle); + + fclose(Handle); + + if (FileSize != STAT.st_size) + { + free(Buffer); + return FALSE; + } + + Handle = fopen(Out, "wb"); + + if (Handle == NULL) + { + free(Buffer); + return FALSE; + } + + FileSize = fwrite(Buffer, 1, STAT.st_size, Handle); + + fclose(Handle); + free(Buffer); + + return TRUE; +} +#endif + +int RefreshMainWindow() +{ + return 0; +} + +int LastSemGets = 0; + +extern int SemHeldByAPI; + +VOID MonitorThread(void * x) +{ + // Thread to detect stuck semaphore + + do + { + if ((Semaphore.Gets == LastSemGets) && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d", + Semaphore.SemProcessID, SemHeldByAPI); + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); +// Debugprintf("Monitor Thread Still going %d %d %d %x %d", LastSemGets, Semaphore.Gets, Semaphore.Flag, Semaphore.SemThreadID, SemHeldByAPI); + + } + while (TRUE); +} + + + + +VOID TIMERINTERRUPT(); + +BOOL Start(); +VOID INITIALISEPORTS(); +Dll BOOL APIENTRY Init_APRS(); +VOID APRSClose(); +Dll VOID APIENTRY Poll_APRS(); +VOID HTTPTimer(); + + +#define CKernel +#include "Versions.h" + +extern struct SEM Semaphore; + +int SemHeldByAPI = 0; +BOOL IGateEnabled = TRUE; +BOOL APRSActive = FALSE; +BOOL ReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; + +BOOL IPActive = FALSE; +extern BOOL IPRequired; + +extern struct WL2KInfo * WL2KReports; + +int InitDone; +char pgm[256] = "LINBPQ"; + +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MUIONLY; +UCHAR MTX; +uint64_t MMASK; + + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +int SAVEPORT = 0; + +VOID SetApplPorts(); + +char VersionString[50] = Verstring; +char VersionStringWithBuild[50]=Verstring; +int Ver[4] = {Vers}; +char TextVerstring[50] = Verstring; + +extern UCHAR PWLen; +extern char PWTEXT[]; +extern int ISPort; + +extern char ChatConfigName[250]; + +BOOL EventsEnabled = 0; + +UCHAR * GetBPQDirectory() +{ + return BPQDirectory; +} +UCHAR * GetLogDirectory() +{ + return LogDirectory; +} +extern int POP3Timer; + +// Console Terminal Stuff + +#ifndef WIN32 + +#define _getch getchar + +/** + Linux (POSIX) implementation of _kbhit(). + Morgan McGuire, morgan@cs.brown.edu + */ + +#include +#include +#include +//#include + +int _kbhit() { + static const int STDIN = 0; + static int initialized = 0; + + if (! initialized) { + // Use termios to turn off line buffering + struct termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = 1; + } + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; +} + +#endif + +void ConTermInput(char * Msg) +{ + int i; + + if (ConTerm.BPQStream == 0) + { + ConTerm.BPQStream = FindFreeStream(); + + if (ConTerm.BPQStream == 255) + { + ConTerm.BPQStream = 0; + printf("No Free Streams\n"); + return; + } + } + + if (!ConTerm.CONNECTED) + SessionControl(ConTerm.BPQStream, 1, 0); + + ConTerm.StackIndex = 0; + + // Stack it + + if (ConTerm.KbdStack[19]) + free(ConTerm.KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + ConTerm.KbdStack[i+1] = ConTerm.KbdStack[i]; + } + + ConTerm.KbdStack[0] = _strdup(ConTerm.kbbuf); + + ConTerm.kbbuf[ConTerm.kbptr]=13; + + SendMsg(ConTerm.BPQStream, ConTerm.kbbuf, ConTerm.kbptr+1); +} + +void ConTermPoll() +{ + int port, sesstype, paclen, maxframe, l4window, len; + int state, change, InputLen, count; + char callsign[11] = ""; + char Msg[300]; + + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + SessionState(ConTerm.BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) + { + // Connected + + ConTerm.CONNECTED = TRUE; + ConTerm.SlowTimer = 0; + } + else + { + ConTerm.CONNECTED = FALSE; + printf("*** Disconnected\n"); + } + } + + GetMsg(ConTerm.BPQStream, Msg, &InputLen, &count); + + if (InputLen) + { + char * ptr = Msg; + char * ptr2 = ptr; + Msg[InputLen] = 0; + + while (ptr) + { + ptr2 = strlop(ptr, 13); + + // Replace CR with CRLF + + printf(ptr); + + if (ptr2) + printf("\r\n"); + + ptr = ptr2; + } + } + + if (_kbhit()) + { + unsigned char c = _getch(); + + if (c == 0xe0) + { + // Cursor control + + c = _getch(); + + if (c == 75) // cursor left + c = 8; + } + +#ifdef WIN32 + printf("%c", c); +#endif + if (c == 8) + { + if (ConTerm.kbptr) + ConTerm.kbptr--; + printf(" \b"); // Already echoed bs - clear typed char from screen + return; + } + + if (c == 13 || c == 10) + { + ConTermInput(ConTerm.kbbuf); + ConTerm.kbptr = 0; + return; + } + + ConTerm.kbbuf[ConTerm.kbptr++] = c; + fflush(NULL); + + } + + return; + +} + + +int Redirected = 0; + +int main(int argc, char * argv[]) +{ + int i; + struct UserInfo * user = NULL; + ConnectionInfo * conn; + struct stat STAT; + PEXTPORTDATA PORTVEC; + UCHAR LogDir[260]; + +#ifdef WIN32 + + WSADATA WsaData; // receives data from WSAStartup + HWND hWnd = GetForegroundWindow(); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); + + // disable the [x] button. + + if (hWnd != NULL) + { + HMENU hMenu = GetSystemMenu(hWnd, 0); + if (hMenu != NULL) + { + DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + DrawMenuBar(hWnd); + } + } + +#else + setlinebuf(stdout); + struct sigaction act; + openlog("LINBPQ", LOG_PID, LOG_DAEMON); +#ifndef MACBPQ +#ifndef FREEBSD + prctl(PR_SET_DUMPABLE, 1); // Enable Core Dumps even with setcap +#endif +#endif + + // Disable Console Terminal if stdout redirected + + if (!isatty(STDOUT_FILENO)) + Redirected = 1; + +#endif + + printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); + printf("%s\n", VerCopyright); + + if (argc > 1 && _stricmp(argv[1], "-v") == 0) + return 0; + + sprintf(RlineVer, "LinBPQ%d.%d.%d", Ver[0], Ver[1], Ver[2]); + + + Debugprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + +#ifndef MACBPQ + _MYTIMEZONE = _timezone; +#endif + + if (_MYTIMEZONE < -86400 || _MYTIMEZONE > 86400) + _MYTIMEZONE = 0; + +#ifdef WIN32 + GetCurrentDirectory(256, BPQDirectory); + GetCurrentDirectory(256, LogDirectory); +#else + getcwd(BPQDirectory, 256); + getcwd(LogDirectory, 256); +#endif + Consoleprintf("Current Directory is %s\n", BPQDirectory); + + for (i = 1; i < argc; i++) + { + if (_memicmp(argv[i], "logdir=", 7) == 0) + { + strcpy(LogDirectory, &argv[i][7]); + break; + } + } + + + // Make sure logs directory exists + + sprintf(LogDir, "%s/logs", LogDirectory); + +#ifdef WIN32 + CreateDirectory(LogDir, NULL); +#else + mkdir(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + if (!ProcessConfig()) + { + WritetoConsoleLocal("Configuration File Error\n"); + return (0); + } + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Linux (", TextVerstring); + +#ifdef MACBPQ + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for MAC (", TextVerstring); +#endif +#ifdef FREEBSD + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for FreeBSD (", TextVerstring); +#endif + + + GetSemaphore(&Semaphore, 0); + + if (Start() != 0) + { + FreeSemaphore(&Semaphore); + return (0); + } + + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + + PWLen=i; + + SetApplPorts(); + + GetUIConfig(); + + INITIALISEPORTS(); + + if (IPRequired) IPActive = Init_IP(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + if (needAIS) + initAIS(); + + RigActive = Rig_Init(); + + FreeSemaphore(&Semaphore); + + OpenReportingSockets(); + + initUTF8(); + + InitDone = TRUE; + + Debugprintf("Monitor Thread ID %x", _beginthread(MonitorThread, 0, 0)); + + +#ifdef WIN32 +#else + openlog("LINBPQ", LOG_PID, LOG_DAEMON); + + memset (&act, '\0', sizeof(act)); + + act.sa_handler = &sigint_handler; + if (sigaction(SIGINT, &act, NULL) < 0) + perror ("SIGINT"); + + act.sa_handler = &sigterm_handler; + if (sigaction(SIGTERM, &act, NULL) < 0) + perror ("sigaction"); + + act.sa_handler = SIG_IGN; + if (sigaction(SIGHUP, &act, NULL) < 0) + perror ("SIGHUP"); + + if (sigaction(SIGPIPE, &act, NULL) < 0) + perror ("SIGPIPE"); + +#endif + + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "chat") == 0) + IncludesChat = TRUE; + } + + if (IncludesChat) + { + RunChat = TRUE; + + printf("Starting Chat\n"); + + sprintf (ChatConfigName, "%s/chatconfig.cfg", BPQDirectory); + printf("Config File is %s\n", ChatConfigName); + + if (stat(ChatConfigName, &STAT) == -1) + { + printf("Chat Config File not found - creating a default config\n"); + ChatApplNum = 2; + MaxChatStreams = 10; + SaveChatConfigFile(ChatConfigName); + } + + if (GetChatConfig(ChatConfigName) == EXIT_FAILURE) + { + printf("Chat Config File seems corrupt - check before continuing\n"); + return -1; + } + + if (ChatApplNum) + { + if (ChatInit() == 0) + { + printf("Chat Init Failed\n"); + RunChat = 0; + } + else + { + printf("Chat Started\n"); + } + } + else + { + printf("Chat APPLNUM not defined\n"); + RunChat = 0; + } + CopyConfigFile(ChatConfigName); + } + + // Start Mail if requested by command line or config + + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "mail") == 0) + IncludesMail = TRUE; + } + + + if (IncludesMail) + { + RunMail = TRUE; + + printf("Starting Mail\n"); + + sprintf (ConfigName, "%s/linmail.cfg", BPQDirectory); + printf("Config File is %s\n", ConfigName); + + if (stat(ConfigName, &STAT) == -1) + { + printf("Config File not found - creating a default config\n"); + strcpy(BBSName, MYNODECALL); + strlop(BBSName, '-'); + strlop(BBSName, ' '); + BBSApplNum = 1; + MaxStreams = 10; + SaveConfig(ConfigName); + } + + if (GetConfig(ConfigName) == EXIT_FAILURE) + { + printf("BBS Config File seems corrupt - check before continuing\n"); + return -1; + } + + printf("Config Processed\n"); + + BBSApplMask = 1<<(BBSApplNum-1); + + // See if we need to warn of possible problem with BaseDir moved by installer + + sprintf(BaseDir, "%s", BPQDirectory); + + + // Set up file and directory names + + strcpy(UserDatabasePath, BaseDir); + strcat(UserDatabasePath, "/"); + strcat(UserDatabasePath, UserDatabaseName); + + strcpy(MsgDatabasePath, BaseDir); + strcat(MsgDatabasePath, "/"); + strcat(MsgDatabasePath, MsgDatabaseName); + + strcpy(BIDDatabasePath, BaseDir); + strcat(BIDDatabasePath, "/"); + strcat(BIDDatabasePath, BIDDatabaseName); + + strcpy(WPDatabasePath, BaseDir); + strcat(WPDatabasePath, "/"); + strcat(WPDatabasePath, WPDatabaseName); + + strcpy(BadWordsPath, BaseDir); + strcat(BadWordsPath, "/"); + strcat(BadWordsPath, BadWordsName); + + strcpy(NTSAliasesPath, BaseDir); + strcat(NTSAliasesPath, "/"); + strcat(NTSAliasesPath, NTSAliasesName); + + strcpy(MailDir, BaseDir); + strcat(MailDir, "/"); + strcat(MailDir, "Mail"); + +#ifdef WIN32 + CreateDirectory(MailDir, NULL); // Just in case +#else + mkdir(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + // Make backup copies of Databases + +// CopyConfigFile(ConfigName); + + CopyBIDDatabase(); + CopyMessageDatabase(); + CopyUserDatabase(); + CopyWPDatabase(); + + SetupMyHA(); + SetupFwdAliases(); + SetupNTSAliases(NTSAliasesPath); + + GetWPDatabase(); + + GetMessageDatabase(); + GetUserDatabase(); + GetBIDDatabase(); + GetBadWordFile(); + GetHTMLForms(); + + // Make sure there is a user record for the BBS, with BBS bit set. + + user = LookupCall(BBSName); + + if (user == NULL) + { + user = AllocateUserRecord(BBSName); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if(SetupNewBBS(user)) + user->flags |= F_BBS; + } + + // if forwarding AMPR mail make sure User/BBS AMPR exists + + if (SendAMPRDirect) + { + BOOL NeedSave = FALSE; + + user = LookupCall("AMPR"); + + if (user == NULL) + { + user = AllocateUserRecord("AMPR"); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + NeedSave = TRUE; + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + NeedSave = TRUE; + } + + if (NeedSave) + SaveUserDatabase(); + } + + + // Make sure SYSOPCALL is set + + if (SYSOPCall[0] == 0) + strcpy(SYSOPCall, BBSName); + + // See if just want to add user (mainly for setup scripts) + + if (argc == 5 && _stricmp(argv[1], "--adduser") == 0) + { + BOOL isBBS = FALSE; + char * response; + + if (_stricmp(argv[4], "TRUE") == 0) + isBBS = TRUE; + + printf("Adding User %s\r\n", argv[2]); + response = AddUser(argv[2], argv[3], isBBS); + printf("%s", response); + exit(0); + } + // Allocate Streams + + strcpy(pgm, "BBS"); + + for (i=0; i < MaxStreams; i++) + { + conn = &Connections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofStreams++; + +// BPQSetHandle(conn->BPQStream, hWnd); + + SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask); + Disconnect(conn->BPQStream); + } + + strcpy(pgm, "LINBPQ"); + + Debugprintf("POP3 Debug Before Init TCP Timer = %d", POP3Timer); + + InitialiseTCP(); + Debugprintf("POP3 Debug Before Init NNTP Timer = %d", POP3Timer); + InitialiseNNTP(); + + SetupListenSet(); // Master set of listening sockets + + if (EnableUI || MailForInterval) + SetupUIInterface(); + + if (MailForInterval) + _beginthread(SendMailForThread, 0, 0); + + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = mktime(tm) - (time_t)_MYTIMEZONE; + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); + + if (LastHouseKeepingTime) + { + if ((now - LastHouseKeepingTime) > MaintInterval * 3600) + { + DoHouseKeeping(FALSE); + } + } + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "tidymail") == 0) + DeleteRedundantMessages(); + + if (_stricmp(argv[i], "nohomebbs") == 0) + DontNeedHomeBBS = TRUE; + } + + printf("Mail Started\n"); + Logprintf(LOG_BBS, NULL, '!', "Mail Starting"); + + } + } + + Debugprintf("POP3 Debug After Mail Init Timer = %d", POP3Timer); + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + +#ifndef WIN32 + + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "daemon") == 0) + { + + // Convert to daemon + + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + + if (pid < 0) + exit(EXIT_FAILURE); + + if (pid > 0) + exit(EXIT_SUCCESS); + + /* Change the file mode mask */ + + umask(0); + + /* Create a new SID for the child process */ + + sid = setsid(); + + if (sid < 0) + exit(EXIT_FAILURE); + + /* Change the current working directory */ + + if ((chdir("/")) < 0) + exit(EXIT_FAILURE); + + /* Close out the standard file descriptors */ + + printf("Entering daemon mode\n"); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + break; + } + } +#endif + + while (KEEPGOING) + { + Sleep(100); + GetSemaphore(&Semaphore, 2); + + if (QCOUNT < 10) + { + if (CLOSING == FALSE) + FindLostBuffers(); + CLOSING = TRUE; + } + + if (CLOSING) + { + if (RunChat) + { + CloseChat(); + RunChat = FALSE; + } + + if (RunMail) + { + int BPQStream, n; + + RunMail = FALSE; + + for (n = 0; n < NumberofStreams; n++) + { + BPQStream = Connections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + +// SaveUserDatabase(); + SaveMessageDatabase(); + SaveBIDDatabase(); + SaveConfig(ConfigName); + } + + KEEPGOING--; // Give time for links to close + setbuf(stdout, NULL); + printf("Closing... %d \r", KEEPGOING); + } + + + if (RigReconfigFlag) + { + RigReconfigFlag = FALSE; + Rig_Close(); + Sleep(2000); // Allow CATPTT threads to close + RigActive = Rig_Init(); + + Consoleprintf("Rigcontrol Reconfiguration Complete"); + } + + if (APRSReconfigFlag) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + Consoleprintf("APRS Reconfiguration Complete"); + } + + if (ReconfigFlag) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + ReconfigFlag = FALSE; + +// SetupBPQDirectory(); + + WritetoConsoleLocal("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { +// SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); +// SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); +// CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + + if (AGWActive) + AGWAPITerminate(); + + WL2KReports = NULL; + +// Sleep(2000); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); + + SetApplPorts(); + + GetUIConfig(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS, IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + +// PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + OpenReportingSockets(); + + WritetoConsoleLocal("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 2); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 2); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + + if (IPActive) Poll_IP(); + if (RigActive) Rig_Poll(); + if (APRSActive) Poll_APRS(); + CheckWL2KReportTimer(); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + if (Redirected == 0) + ConTermPoll(); + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + + HTTPTimer(); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + + Slowtimer++; + + if (RunChat) + { + ChatPollStreams(); + ChatTrytoSend(); + + if (Slowtimer > 100) // 10 secs + { + ChatTimer(); + } + } + + if (RunMail) + { + PollStreams(); + + if (Slowtimer > 100) // 10 secs + { + time_t NOW = time(NULL); + struct tm * tm; + + TCPTimer(); + FWDTimerProc(); + BBSSlowTimer(); + + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + LastTrafficTime = NOW; + CreateBBSTrafficReport(); + } + } + } + TCPFastTimer(); + TrytoSend(); + } + + if (Slowtimer > 100) + Slowtimer = 0; + } + + printf("Closing Ports\n"); + + CloseTNCEmulator(); + + if (AGWActive) + AGWAPITerminate(); + + if (needAIS) + SaveAIS(); + + // Close Ports + + PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5, PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + if (AUTOSAVE) + SaveNodes(); + + if (AUTOSAVEMH) + SaveMH(); + + if (IPActive) + IPClose(); + + if (RunMail) + FreeWebMailMallocs(); + + upnpClose(); + + // Close any open logs + + for (i = 0; i < 4; i++) + { + if (LogHandle[i]) + fclose(LogHandle[i]); + } + + return 0; +} + +int APIENTRY WritetoConsole(char * buff) +{ + return WritetoConsoleLocal(buff); +} + +int WritetoConsoleLocal(char * buff) +{ + return printf("%s", buff); +} + +#ifdef WIN32 +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +#endif +//UINT SoundModemExtInit(EXTPORTDATA * PortEntry); +//UINT BaycomExtInit(EXTPORTDATA * PortEntry); + +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); + +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); + +void * TelnetExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); + +void * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + // Only works with built in drivers + + UCHAR Value[20]; + + strcpy(Value,PORTVEC->PORT_DLL_NAME); + + _strupr(Value); + +#ifndef FREEBSD +#ifndef MACBPQ + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; +#endif +#endif + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + +#ifdef WIN32 + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + +#endif +/* + if (strstr(Value, "SOUNDMODEM")) + return (UINT) SoundModemExtInit; + + if (strstr(Value, "BAYCOM")) + return (UINT) BaycomExtInit; +*/ + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + return(0); +} + +int APIENTRY Restart() +{ + CLOSING = TRUE; + return TRUE; +} + +int APIENTRY Reboot() +{ + // Run sudo shutdown -r -f +#ifdef WIN32 + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); + return 0; +#else + + char * arg_list[] = {NULL, NULL, NULL, NULL, NULL}; + pid_t child_pid; + char * Context; + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + arg_list[0] = "sudo"; + arg_list[1] = "shutdown"; + arg_list[2] = "now"; + arg_list[3] = "-r"; + + // Fork and Exec shutdown + + // Duplicate this process. + + child_pid = fork(); + + if (child_pid == -1) + { + printf ("Reboot fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to run shutdown\n"); + exit(0); // Kill the new process + } + return TRUE; +#endif + +} + +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsoleLocal("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsoleLocal("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} + +int APRSWriteLog(char * msg); + +VOID MonitorAPRSIS(char * Msg, size_t MsgLen, BOOL TX) +{ + char Line[300]; + char Copy[300]; + int Len; + struct tm * TM; + time_t NOW; + + if (LogAPRSIS == 0) + return; + + if (MsgLen > 250) + return; + + // Mustn't change Msg + + memcpy(Copy, Msg, MsgLen); + Copy[MsgLen] = 0; + + NOW = time(NULL); + TM = gmtime(&NOW); + + Len = sprintf_s(Line, 299, "%02d:%02d:%02d%c %s", TM->tm_hour, TM->tm_min, TM->tm_sec, (TX)? 'T': 'R', Copy); + + APRSWriteLog(Line); + +} + +struct TNCINFO * TNC; + +#ifndef WIN32 + +#include +#include + +#ifndef MACBPQ +#ifdef __MACH__ + +#include + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 0 + +int clock_gettime(int clk_id, struct timespec *t){ + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + uint64_t time; + time = mach_absolute_time(); + double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom); + double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9); + t->tv_sec = seconds; + t->tv_nsec = nseconds; + return 0; +} +#endif +#endif + +int GetTickCount() +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); +} + + + +void SetWindowText(HWND hWnd, char * lpString) +{ + return; +}; + +BOOL SetDlgItemText(HWND hWnd, int item, char * lpString) +{ + return 0; +}; + +#endif + +int GetListeningPortsPID(int Port) +{ +#ifdef WIN32 + + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + unsigned int n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } +#endif + return 0; // Not found +} + + + +VOID Check_Timer() +{ +} + +VOID POSTDATAAVAIL(){}; + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(6425,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,255,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,255,255) +}; + + +//VOID SendRPBeacon(struct TNCINFO * TNC) +//{ +//} + +int PollStreams() +{ + int state,change; + ConnectionInfo * conn; + int n; + struct UserInfo * user = NULL; + char ConnectedMsg[] = "*** CONNECTED "; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + DoReceivedData(conn->BPQStream); + DoBBSMonitorData(conn->BPQStream); + + SessionState(conn->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + Connected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + Disconnected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + } + } + + return 0; +} + + +VOID CloseConsole(int Stream) +{ +} + +#ifndef WIN32 + +int V4ProcessReceivedData(struct TNCINFO * TNC) +{ + return 0; +} +#endif + +#ifdef FREEBSD + +char * gcvt(double _Val, int _NumOfDigits, char * _DstBuf) +{ + sprintf(_DstBuf, "%f", _Val); + return _DstBuf; +} + +#endif + + + + + diff --git a/MCP2221.c b/MCP2221.c new file mode 100644 index 0000000..233888a --- /dev/null +++ b/MCP2221.c @@ -0,0 +1,427 @@ +// MCP2221.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "time.h" +#include "winstdint.h" + +#include "hidapi.h" +void DecodeCM108(int Port, char * ptr); + +#ifdef WIN32 + +/* Simple Raw HID functions for Windows - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include +#include +//#include +#include +#include +//#include +//#include + +typedef USHORT USAGE; + + +typedef struct _HIDD_CONFIGURATION { + PVOID cookie; + ULONG size; + ULONG RingBufferSize; +} HIDD_CONFIGURATION, *PHIDD_CONFIGURATION; + +typedef struct _HIDD_ATTRIBUTES { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + +typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + + +typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA; + + + +// a list of all opened HID devices, so the caller can +// simply refer to them by number +typedef struct hid_struct hid_t; +static hid_t *first_hid = NULL; +static hid_t *last_hid = NULL; +struct hid_struct { + HANDLE handle; + int open; + struct hid_struct *prev; + struct hid_struct *next; +}; +static HANDLE rx_event=NULL; +static HANDLE tx_event=NULL; +static CRITICAL_SECTION rx_mutex; +static CRITICAL_SECTION tx_mutex; + + +// private functions, not intended to be used from outside this file +static void add_hid(hid_t *h); +static hid_t * get_hid(int num); +static void free_all_hid(void); +void print_win32_err(void); + + + + +// rawhid_recv - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int rawhid_recv(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD r; + int n; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&rx_mutex); + ResetEvent(&rx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = rx_event; + if (!ReadFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(rx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&rx_mutex); + if (n <= 0) return -1; + n--; + if (n > len) n = len; + memcpy(buf, tmpbuf + 1, n); + return n; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&rx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&rx_mutex); + return -1; +} + +// rawhid_send - send a packet +// Inputs: +// num = device to transmit to (zero based) +// buf = buffer containing packet to send +// len = number of bytes to transmit +// timeout = time to wait, in milliseconds +// Output: +// number of bytes sent, or -1 on error +// +int rawhid_send(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&tx_mutex); + ResetEvent(&tx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = tx_event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(tx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&tx_mutex); + if (n <= 0) return -1; + return n - 1; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&tx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&tx_mutex); + return -1; +} + +HANDLE rawhid_open(char * Device) +{ + DWORD index=0; + HANDLE h; + hid_t *hid; + int count=0; + + if (first_hid) free_all_hid(); + + if (!rx_event) + { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + h = CreateFile(Device, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + if (h == INVALID_HANDLE_VALUE) + return 0; + + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) + { + CloseHandle(h); + return 0; + } + hid->handle = h; + hid->open = 1; + add_hid(hid); + + return h; +} + + +// rawhid_close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void rawhid_close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + + CloseHandle(hid->handle); + hid->handle = NULL; + hid->open = FALSE; +} + + + + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + for (p = first_hid; p; p = p->next) + { + CloseHandle(p->handle); + p->handle = NULL; + p->open = FALSE; + } + p = first_hid; + while (p) { + q = p; + p = p->next; + free(q); + } + first_hid = last_hid = NULL; +} + + + +void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + 0, buf, sizeof(buf), NULL); + printf("err %ld: %s\n", err, buf); +} + +#endif + +HANDLE hDevice; + +char * HIDDevice; + +int main() +{ + int Len; + unsigned char Msg[65] = ""; + + DecodeCM108(0, "0x4D8:0xDD"); + + hDevice = rawhid_open(HIDDevice); + + if (hDevice) + printf("Rigcontrol HID Device %s opened\n", HIDDevice); + + Msg[0] = 0x51; + Msg[0] = 0xB0; + Msg[1] = 0x1; + + Len = rawhid_send(0, Msg, 64, 100); + + Msg[0] = 0; + +#ifdef WIN32 + Len = rawhid_recv(0, Msg, 64, 100); +#else + Len = read(PORT->hDevice, Msg, 64); +#endif + + + return 0; +} + + +char * CM108Device = NULL; + +void DecodeCM108(int Port, char * ptr) +{ + // Called if Device Name or PTT = Param is CM108 + +#ifdef WIN32 + + // Next Param is VID and PID - 0xd8c:0x8 or Full device name + // On Windows device name is very long and difficult to find, so + // easier to use VID/PID, but allow device in case more than one needed + + char * next; + int32_t VID = 0, PID = 0; + char product[256]; + char sernum[256] = "NULL"; + + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + if (strlen(ptr) > 16) + CM108Device = _strdup(ptr); + else + { + VID = strtol(ptr, &next, 0); + if (next) + PID = strtol(++next, &next, 0); + + // Look for Device + + devs = hid_enumerate(0, 0); // so we list devices(USHORT)VID, (USHORT)PID); + cur_dev = devs; + while (cur_dev) + { + wcstombs(product, cur_dev->product_string, 255); + if (cur_dev->serial_number) + wcstombs(sernum, cur_dev->serial_number, 255); + + if (product) + printf("HID Device %s VID %X PID %X Ser %s %s\n", product, cur_dev->vendor_id, cur_dev->product_id, sernum, cur_dev->path); + else + printf("HID Device %s VID %X PID %X Ser %s %s", "Missing Product\n", cur_dev->vendor_id, cur_dev->product_id, sernum, cur_dev->path); + + if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) + path_to_open = cur_dev->path; + + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + HIDDevice = _strdup(path_to_open); + } + hid_free_enumeration(devs); + } +#else + + // Linux - Next Param HID Device, eg /dev/hidraw0 + + CM108Device = _strdup(ptr); +#endif + } + + diff --git a/MCP2221.vcproj b/MCP2221.vcproj new file mode 100644 index 0000000..d2c9621 --- /dev/null +++ b/MCP2221.vcproj @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MCP2221.vcproj.DESKTOP-TGEL8RC.John.user b/MCP2221.vcproj.DESKTOP-TGEL8RC.John.user new file mode 100644 index 0000000..40182c4 --- /dev/null +++ b/MCP2221.vcproj.DESKTOP-TGEL8RC.John.user @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/MailNode.vcxproj.filters b/MailNode.vcxproj.filters index 23094c6..9f953ec 100644 --- a/MailNode.vcxproj.filters +++ b/MailNode.vcxproj.filters @@ -1,324 +1,324 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Source Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + \ No newline at end of file diff --git a/MailNode.vcxproj.user b/MailNode.vcxproj.user index c9aaeaf..448994b 100644 --- a/MailNode.vcxproj.user +++ b/MailNode.vcxproj.user @@ -1,8 +1,8 @@ - - - - C:\Dev\Msdev2005\projects\bpq32\BPQMail\x64\Debug\LinBPQ.exe - c:\linbpq - WindowsLocalDebugger - + + + + C:\Dev\Msdev2005\projects\bpq32\BPQMail\x64\Debug\LinBPQ.exe + c:\linbpq + WindowsLocalDebugger + \ No newline at end of file diff --git a/PG/Loop.c b/PG/Loop.c index ac753d9..6a5d66e 100644 --- a/PG/Loop.c +++ b/PG/Loop.c @@ -1,24 +1,24 @@ -#include -#include - -/* - * TST_PG.C - * - * Little test program of "PG" command for FBB BBS software. - * - * (C) F6FBB 1991. - * - * FBB software 5.14 and up. - * - * - * This program echoes to the user what he types - * or executes a BBS command preceded by "CMD" - * until "BYE" is received - */ - - -main(int argc, char **argv) -{ - Sleep(10000); - return 0; +#include +#include + +/* + * TST_PG.C + * + * Little test program of "PG" command for FBB BBS software. + * + * (C) F6FBB 1991. + * + * FBB software 5.14 and up. + * + * + * This program echoes to the user what he types + * or executes a BBS command preceded by "CMD" + * until "BYE" is received + */ + + +main(int argc, char **argv) +{ + Sleep(10000); + return 0; } \ No newline at end of file diff --git a/PG/PGTest.c b/PG/PGTest.c index 4d3742d..43dcac4 100644 --- a/PG/PGTest.c +++ b/PG/PGTest.c @@ -1,53 +1,53 @@ -#include - -/* - * TST_PG.C - * - * Little test program of "PG" command for FBB BBS software. - * - * (C) F6FBB 1991. - * - * FBB software 5.14 and up. - * - * - * This program echoes to the user what he types - * or executes a BBS command preceded by "CMD" - * until "BYE" is received - */ - - -main(int argc, char **argv) -{ - int i; - int level = atoi(argv[2]); /* Get level from argument list */ - - /* and transform it to integer */ - if (level == 0) { /* Is level equal to 0 ? */ - /* This is the first call */ - printf("Hello %s, type BYE when you want to stop !\n", argv[1]); - return(1); /* program will be called again */ - } - else { - strupr(argv[5]); /* Capitalise the first word */ - if (strcmp(argv[5], "BYE") == 0) { /* is BYE received ? */ - printf("Ok, bye-bye\n"); - return(0); /* Yes, go on BBS */ - } - else if (strcmp(argv[5], "CMD") == 0) { /* is CMD received ? */ - for (i = 6 ; i < argc ; i++) /* List line arguments */ - printf("%s ", argv[i]); /* sent by user */ - putchar('\n'); - for (i = 6 ; i < argc ; i++) /* List line arguments */ - printf("%s ", argv[i]); /* sent by user */ - putchar('\n'); - return(4); /* Yes, send command */ - } - else { - printf("You told me : "); /* These are other lines */ - for (i = 5 ; i < argc ; i++) /* List line arguments */ - printf("%s ", argv[i]); /* sent by user */ - putchar('\n'); - return(1); /* No, call again program */ - } - } +#include + +/* + * TST_PG.C + * + * Little test program of "PG" command for FBB BBS software. + * + * (C) F6FBB 1991. + * + * FBB software 5.14 and up. + * + * + * This program echoes to the user what he types + * or executes a BBS command preceded by "CMD" + * until "BYE" is received + */ + + +main(int argc, char **argv) +{ + int i; + int level = atoi(argv[2]); /* Get level from argument list */ + + /* and transform it to integer */ + if (level == 0) { /* Is level equal to 0 ? */ + /* This is the first call */ + printf("Hello %s, type BYE when you want to stop !\n", argv[1]); + return(1); /* program will be called again */ + } + else { + strupr(argv[5]); /* Capitalise the first word */ + if (strcmp(argv[5], "BYE") == 0) { /* is BYE received ? */ + printf("Ok, bye-bye\n"); + return(0); /* Yes, go on BBS */ + } + else if (strcmp(argv[5], "CMD") == 0) { /* is CMD received ? */ + for (i = 6 ; i < argc ; i++) /* List line arguments */ + printf("%s ", argv[i]); /* sent by user */ + putchar('\n'); + for (i = 6 ; i < argc ; i++) /* List line arguments */ + printf("%s ", argv[i]); /* sent by user */ + putchar('\n'); + return(4); /* Yes, send command */ + } + else { + printf("You told me : "); /* These are other lines */ + for (i = 5 ; i < argc ; i++) /* List line arguments */ + printf("%s ", argv[i]); /* sent by user */ + putchar('\n'); + return(1); /* No, call again program */ + } + } } \ No newline at end of file diff --git a/RigControl.c b/RigControl.c index 6a7d026..83be639 100644 --- a/RigControl.c +++ b/RigControl.c @@ -3592,7 +3592,7 @@ ok: else { if (!PORT->AutoPoll) - SendResponse(RIG->Session, "Frequency Set OK"); + SendResponse(RIG->Session, "Frequency Set OK"); PORT->Timeout = 0; } @@ -5286,6 +5286,7 @@ void DecodeCM108(int Port, char * ptr) char * next; int32_t VID = 0, PID = 0; char product[256]; + char sernum[256] = "NULL"; struct hid_device_info *devs, *cur_dev; const char *path_to_open = NULL; @@ -5306,11 +5307,13 @@ void DecodeCM108(int Port, char * ptr) while (cur_dev) { wcstombs(product, cur_dev->product_string, 255); + if (cur_dev->serial_number) + wcstombs(sernum, cur_dev->serial_number, 255); if (product) - Debugprintf("HID Device %s VID %X PID %X %s", product, cur_dev->vendor_id, cur_dev->product_id, cur_dev->path); + Debugprintf("HID Device %s VID %X PID %X Ser %s %s", product, cur_dev->vendor_id, cur_dev->product_id, sernum, cur_dev->path); else - Debugprintf("HID Device %s VID %X PID %X %s", "Missing Product", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path); + Debugprintf("HID Device %s VID %X PID %X Ser %s %s", "Missing Product", cur_dev->vendor_id, cur_dev->product_id, sernum, cur_dev->path); if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) path_to_open = cur_dev->path; diff --git a/SCSPactor.c b/SCSPactor.c index 244a62c..14c82c6 100644 --- a/SCSPactor.c +++ b/SCSPactor.c @@ -552,6 +552,12 @@ ok: txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + if (txlen == 1 && buff->L2DATA[0] == 0) // Keepalive Packet + { + ReleaseBuffer(buffptr); + return 0; + } + buffptr->Len = txlen; memcpy(buffptr->Data, buff->L2DATA, txlen); @@ -1092,6 +1098,56 @@ void SCSCheckRX(struct TNCINFO * TNC) // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is // complete. If CRC is duff, we will eventually time out and get a retry. The retry code // can clear the RC buffer + + if (TNC->UsingTermMode) + { + // Send response to Host if connected + + PMSGWITHLEN buffptr; + int Len = TNC->RXLen; + int Posn = 0; + + // First message is probably ACK of JHO4T - AA AA 1F 00 1E 19 + + if (TNC->RXBuffer[0] == 0xaa && Len > 6) + { + memmove(TNC->RXBuffer, &TNC->RXBuffer[6], Len - 6); + Len -= 6; + } + + // TNC seems to send 1e f7 or 1e 87 regularly + + while (TNC->RXBuffer[0] == 0x1e && Len > 1) + { + memmove(TNC->RXBuffer, &TNC->RXBuffer[2], Len - 2); + Len -= 2; + } + + if (Len == 0) + { + TNC->RXLen = 0; // Ready for next frame + return; + } + + while (Len > 250) + { + buffptr = GetBuff(); + buffptr->Len = 250; + memcpy(buffptr->Data, &TNC->RXBuffer[Posn], 250); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + Len -= 250; + Posn += 250; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, &TNC->RXBuffer[Posn], Len); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + + TNC->RXLen = 0; // Ready for next frame + return; + } + if (TNC->RXBuffer[0] != 170) { @@ -1247,6 +1303,51 @@ VOID SCSPoll(int Port) int nn; struct STREAMINFO * STREAM; + if (TNC->UsingTermMode) + { + if (TNC->Streams[Stream].BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + + // See if enter host mode command + + if (_memicmp(buffptr->Data, "ENTERHOST\r", buffptr->Len) == 0) + { + TNC->UsingTermMode = FALSE; + + memcpy(Poll, "JHOST4\r", 7); + TNC->TXLen = 7; + WriteCommBlock(TNC); + + // No response expected + + Sleep(10); + + Poll[2] = 255; // Channel + TNC->Toggle = 0; + Poll[3] = 0x41; + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + CRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + TNC->Timeout = 5; // 1/2 sec - In case missed + + + } + else + { + // Send to TNC + + memcpy(&Poll[0], buffptr->Data, buffptr->Len); + TNC->TXLen = buffptr->Len;; + WriteCommBlock(TNC); + } + ReleaseBuffer(buffptr); + } + return; + } + if (TNC->MinLevelTimer) { TNC->MinLevelTimer--; @@ -1721,6 +1822,10 @@ VOID SCSPoll(int Port) // But we cant set digipeated bit in call, so if we find one, skip message + // This doesn't seem to work + +/* + ConvFromAX25(Buffer + 7, ICall); // Origin strlop(ICall, ' '); @@ -1762,7 +1867,7 @@ VOID SCSPoll(int Port) 1, Buffer, // Flag CmdSet as Data 2, TNC->NodeCall); // Flag as Chan 0 Command } - +*/ ReleaseBuffer((UINT *)buffptr); return; } @@ -2006,6 +2111,24 @@ VOID SCSPoll(int Port) return; } + + if ((Stream == 0) && memcmp(Buffer, "EXITHOST", 8) == 0) + { + UCHAR * Poll = TNC->TXBuffer; + + TNC->UsingTermMode = 1; + + ExitHost(TNC); + + // Send CR to get prompt from TNC + + Poll[0] = 13; + TNC->TXLen = 1; + WriteCommBlock(TNC); + + ReleaseBuffer(buffptr); + return; + } if (Stream == 0 && Buffer[0] == 'C' && datalen > 2) // Pactor Connect Poll[2] = TNC->Streams[0].DEDStream = 31; // Pactor Channel @@ -2086,8 +2209,7 @@ VOID SCSPoll(int Port) return; } } - - // Anything else send to tnc. + Poll[4] = datalen - 1; memcpy(&Poll[5], buffptr->Data, datalen); diff --git a/SCSTracker.c b/SCSTracker.c index c7d9f93..934759f 100644 --- a/SCSTracker.c +++ b/SCSTracker.c @@ -290,6 +290,9 @@ ConfigLine: if (_memicmp(buf, "UPDATEMAP", 9) == 0) TNC->PktUpdateMap = TRUE; else + if (_memicmp(buf, "TeensyRPR", 9) == 0) + TNC->TeensyRPR = TRUE; + else if (_memicmp(buf, "WL2KREPORT", 10) == 0) TNC->WL2K = DecodeWL2KReportLine(buf); else @@ -1655,10 +1658,12 @@ VOID TrkExitHost(struct TNCINFO * TNC) TNC->TXBuffer[0] = 1; TNC->TXBuffer[1] = 1; TNC->TXBuffer[2] = 1; - memcpy(&TNC->TXBuffer[3], "%R", 2); - - StuffAndSend(TNC, Poll, 5); + if (!TNC->TeensyRPR) // %R puts TNC into Program Mode. + { + memcpy(&TNC->TXBuffer[3], "%R", 2); + StuffAndSend(TNC, Poll, 5); + } return; } diff --git a/TelnetV6.c b/TelnetV6.c index c67516c..cbf8c4f 100644 --- a/TelnetV6.c +++ b/TelnetV6.c @@ -342,7 +342,7 @@ BOOL SendAndCheck(struct ConnectionInfo * sockptr, unsigned char * MsgPtr, int l VOID SendPortsForMonitor(SOCKET sock, int Secure) { - UCHAR PortInfo[1500] = {0xff, 0xff}; + UCHAR PortInfo[3000] = {0xff, 0xff}; UCHAR * ptr = &PortInfo[2]; char ID[31] = ""; struct PORTCONTROL * PORT; diff --git a/UIRoutines.c b/UIRoutines.c index ebf56a8..014fe01 100644 --- a/UIRoutines.c +++ b/UIRoutines.c @@ -32,14 +32,14 @@ static char MAILMYCALL[7]; #pragma pack(1) -UINT UIPortMask = 0; -BOOL UIEnabled[33]; -BOOL UIMF[33]; -BOOL UIHDDR[33]; -BOOL UINull[33]; -char * UIDigi[33]; -char * UIDigiAX[33]; // ax.25 version of digistring -int UIDigiLen[33]; // Length of AX string +uint64_t UIPortMask = 0; +BOOL UIEnabled[MaxBPQPortNo + 1]; +BOOL UIMF[MaxBPQPortNo + 1]; +BOOL UIHDDR[MaxBPQPortNo + 1]; +BOOL UINull[MaxBPQPortNo + 1]; +char * UIDigi[MaxBPQPortNo + 1]; +char * UIDigiAX[MaxBPQPortNo + 1]; // ax.25 version of digistring +int UIDigiLen[MaxBPQPortNo + 1]; // Length of AX string @@ -52,14 +52,13 @@ struct SEM DGSemaphore = {0, 0}; // For locking access to DG_Q; VOID UnQueueRaw(void * Param); static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue); -DllExport char * APIENTRY GetApplName(int Appl); - -int APIENTRY SendRaw(int port, char * msg, int len); +char * APIENTRY GetApplName(int Appl); int APIENTRY GetNumberofPorts(); +int APIENTRY GetPortNumber(int portslot); VOID SetupUIInterface() { - int i, NumPorts = GetNumberofPorts(); + int i; #ifndef LINBPQ struct _EXCEPTION_POINTERS exinfo; #endif @@ -70,13 +69,13 @@ VOID SetupUIInterface() UIPortMask = 0; - for (i = 1; i <= NumPorts; i++) + for (i = 1; i <= MaxBPQPortNo; i++) { if (UIEnabled[i]) { char DigiString[100], * DigiLeft; - UIPortMask |= 1 << (i-1); + UIPortMask |= (uint64_t)1 << (i-1); UIDigiLen[i] = 0; if (UIDigi[i]) @@ -122,7 +121,7 @@ VOID Free_UI() int i; PMESSAGEX AXMSG; - for (i = 1; i <= 32; i++) + for (i = 1; i <= MaxBPQPortNo; i++) { if (UIDigi[i]) { @@ -179,18 +178,25 @@ VOID SendMsgUI(struct MsgInfo * Msg) { char msg[200]; int len, i; - int Mask = UIPortMask; - int NumPorts = GetNumberofPorts(); + uint64_t Mask = UIPortMask; //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + char Via[80] = ""; struct tm *tm = gmtime((time_t *)&Msg->datecreated); - - len = sprintf_s(msg, sizeof(msg),"%-6d %c %6d %-13s %-6s %02d%02d%02d %s\r", - Msg->number, Msg->type, Msg->length, Msg->to, + + if (Msg->via[0]) + { + Via[0] = '@'; + strcpy(&Via[1], Msg->via); + strlop(Via, '.'); // Only show first part of via + } + + len = sprintf_s(msg, sizeof(msg),"%-6d %c %6d %-6s%-7s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->length, Msg->to, Via, Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); - for (i=1; i <= NumPorts; i++) + for (i=1; i <= MaxBPQPortNo; i++) { if ((Mask & 1) && UIHDDR[i]) Send_AX_Datagram(msg, len, i, AXDEST, TRUE); @@ -212,10 +218,19 @@ VOID SendHeaders(int Number, int Port) while (Number <= LatestMsg) { + char Via[80] = ""; + Msg = FindMessageByNumber(Number); if (Msg) { + if (Msg->via[0]) + { + Via[0] = '@'; + strcpy(&Via[1], Msg->via); + strlop(Via, '.'); // Only show first part of via + } + if (len > (200 - strlen(Msg->title))) { Send_AX_Datagram(msg, len, Port, AXDEST, FALSE); @@ -224,8 +239,8 @@ VOID SendHeaders(int Number, int Port) tm = gmtime((time_t *)&Msg->datecreated); - len += sprintf(&msg[len], "%-6d %c %6d %-13s %-6s %02d%02d%02d %s\r", - Msg->number, Msg->type, Msg->length, Msg->to, + len += sprintf(&msg[len], "%-6d %c %6d %-6s%-7s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->length, Msg->to, Via, Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); } else @@ -248,12 +263,11 @@ VOID SendDummyUI(int num) { char msg[100]; int len, i; - int Mask = UIPortMask; - int NumPorts = GetNumberofPorts() -; + uint64_t Mask = UIPortMask; + len = sprintf_s(msg, sizeof(msg),"%-6d #\r", num); - for (i=1; i <= NumPorts; i++) + for (i=1; i <= MaxBPQPortNo; i++) { if (Mask & 1) Send_AX_Datagram(msg, len, i, AXDEST, TRUE); @@ -266,9 +280,8 @@ VOID SendLatestUI(int Port) { char msg[20]; int len, i; - int Mask = UIPortMask; - int NumPorts = GetNumberofPorts(); - + uint64_t Mask = UIPortMask; + len = sprintf_s(msg, sizeof(msg),"%-6d !!\r", LatestMsg); if (Port) @@ -277,12 +290,12 @@ VOID SendLatestUI(int Port) return; } - for (i=1; i <= NumPorts; i++) + for (i = 1; i <= MaxBPQPortNo; i++) { - if ((Mask & 1) && UIHDDR[i]) + if ((Mask & (uint64_t)1) && UIHDDR[i]) Send_AX_Datagram(msg, len, i, AXDEST, TRUE); - Mask>>=1; + Mask >>= 1; } } @@ -324,9 +337,7 @@ static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, QueueRaw(Port, &AXMSG, Len + 16); else SendRaw(Port, (char *)&AXMSG.DEST, Len + 16); - - return; - + } VOID UnQueueRaw(void * Param) @@ -366,6 +377,20 @@ VOID ProcessUItoFBB(char * msg, int len, int Port) int Number, Sum, Sent = 0; char cksum[3]; + int n, i; + + // Send_AX_Datagram uses Port Slot, not Port Number + + for (n = 1 ; n <= GetNumberofPorts(); n++) + { + i = GetPortNumber(n); + + if (i == Port) + { + Port = n; + break; + } + } if (msg[0] == '?') { @@ -523,8 +548,7 @@ VOID ExpandMailFor() VOID SendMailFor(char * Msg, BOOL HaveCalls) { - int Mask = UIPortMask; - int NumPorts = GetNumberofPorts(); + uint64_t Mask = UIPortMask; int i; if (!HaveCalls) @@ -532,7 +556,7 @@ VOID SendMailFor(char * Msg, BOOL HaveCalls) Sleep(1000); - for (i=1; i <= NumPorts; i++) + for (i=1; i <= MaxBPQPortNo; i++) { if (Mask & 1) { diff --git a/UZ7HODrv.c b/UZ7HODrv.c index 7ec055b..50e5cf8 100644 --- a/UZ7HODrv.c +++ b/UZ7HODrv.c @@ -2914,12 +2914,14 @@ UZ7HO T GM8BPQ-2 APRS 1:Fm GM8BPQ-2 To APRS Via WIDE2-2 [1 AdjMsg = &Monframe; // Adjusted for digis ptr = strstr(Msg, "Fm "); + if (ptr == 0) return; ConvToAX25(&ptr[3], Monframe.ORIGIN); memcpy(MHCall, &ptr[3], 11); strlop(MHCall, ' '); ptr = strstr(ptr, "To "); + if (ptr == 0) return; ConvToAX25(&ptr[3], Monframe.DEST); @@ -2934,6 +2936,8 @@ UZ7HO T GM8BPQ-2 APRS 1:Fm GM8BPQ-2 To APRS Via WIDE2-2 [1 memcpy(Save, &ptr[4], 60); ptr = strtok_s(Save, ", ", &context); + if (ptr == 0) return; + DigiLoop: temp = (char *)AdjMsg; @@ -2952,6 +2956,7 @@ DigiLoop: AdjMsg->ORIGIN[6] |= 0x80; // Set end of address ptr = strtok_s(NULL, ", ", &context); + if (ptr == 0) return; if (ptr[0] != '<') goto DigiLoop; @@ -3012,18 +3017,21 @@ DigiLoop: if (memcmp(&ptr[1], "RR", 2) == 0) { nrptr = strchr(&ptr[3], '>'); + if (nrptr == 0) return; AdjMsg->CTL = 0x1 | (nrptr[-2] << 5); } else if (memcmp(&ptr[1], "RNR", 3) == 0) { nrptr = strchr(&ptr[4], '>'); + if (nrptr == 0) return; AdjMsg->CTL = 0x5 | (nrptr[-2] << 5); } else if (memcmp(&ptr[1], "REJ", 3) == 0) { nrptr = strchr(&ptr[4], '>'); + if (nrptr == 0) return; AdjMsg->CTL = 0x9 | (nrptr[-2] << 5); } else @@ -3058,12 +3066,18 @@ DigiLoop: if ((AdjMsg->CTL & 1) == 0 || AdjMsg->CTL == 3) // I or UI { ptr = strstr(ptr, "pid"); + if (ptr == 0) return; + sscanf(&ptr[4], "%x", (unsigned int *)&AdjMsg->PID); ptr = strstr(ptr, "Len"); + if (ptr == 0) return; + ILen = atoi(&ptr[4]); ptr = strstr(ptr, "]"); + if (ptr == 0) return; + ptr += 2; // Skip ] and cr memcpy(AdjMsg->L2DATA, ptr, ILen); Monframe.LENGTH += ILen; @@ -3071,6 +3085,8 @@ DigiLoop: else if (AdjMsg->CTL == 0x97) // FRMR { ptr = strstr(ptr, ">"); + if (ptr == 0) return; + sscanf(ptr+1, "%hhx %hhx %hhx", &AdjMsg->PID, &AdjMsg->L2DATA[0], &AdjMsg->L2DATA[1]); Monframe.LENGTH += 3; } diff --git a/VARA.c b/VARA.c index cf6dd7d..983f77d 100644 --- a/VARA.c +++ b/VARA.c @@ -2269,6 +2269,13 @@ VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) return; } + if (_memicmp(Buffer, "ENCRYPTION ", 11) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + if (_memicmp(Buffer, "MISSING SOUNDCARD", 17) == 0) { strcat(Buffer, "\r"); diff --git a/Versions.h b/Versions.h index f3bfe9d..5ebd8fc 100644 --- a/Versions.h +++ b/Versions.h @@ -10,14 +10,14 @@ #endif -#define KVers 6,0,23,77 -#define KVerstring "6.0.23.77\0" +#define KVers 6,0,23,81 +#define KVerstring "6.0.23.81\0" #ifdef CKernel #define Vers KVers #define Verstring KVerstring -#define Datestring "May 2023" +#define Datestring "July 2023" #define VerComments "G8BPQ Packet Switch (C Version)" KVerstring #define VerCopyright "Copyright © 2001-2023 John Wiseman G8BPQ\0" #define VerDesc "BPQ32 Switch\0" diff --git a/bpqmail.h b/bpqmail.h index ab1052f..d910932 100644 --- a/bpqmail.h +++ b/bpqmail.h @@ -34,6 +34,8 @@ #include "asmstrucs.h" +#define MaxBPQPortNo 63 // Port 64 reserved for BBS Mon +#define MAXBPQPORTS 63 #define NEWROUTING diff --git a/bpqmail.h.bak b/bpqmail.h.bak index a153c3f..eb7323a 100644 --- a/bpqmail.h.bak +++ b/bpqmail.h.bak @@ -1,1604 +1,1604 @@ -#ifndef WINVER // Allow use of features specific to Windows XP or later. -#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. -#endif - -#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. -#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. -#endif - -#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. -#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. -#endif - -#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. -#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. -#endif - - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#define _CRT_SECURE_NO_DEPRECATE -#define _WINSOCK_DEPRECATED_NO_WARNINGS - -#define LIBCONFIG_STATIC -#include - -#include "compatbits.h" - -#ifndef LINBPQ -#include "bpq32.h" -#include "BPQMailrc.h" -#include "dbghelp.h" -#else -#include "CHeaders.h" -#endif - -#include "asmstrucs.h" - - -#define NEWROUTING - - - -// Standard __except handler for try/except - -VOID CheckProgramErrors(); -VOID WriteMiniDump(); - -extern int ProgramErrors; - -extern struct _EXCEPTION_POINTERS exinfox; - -#ifdef WIN32 -Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg); - -#define My__except_Routine(Message) \ -__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ -{\ - Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ - exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ - exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ - exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ - CheckProgramErrors();\ - WriteMiniDump();\ -} - - -/* -#define My__except_Routine(Message) \ -__except(memcpy(&exinfox, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ -{\ - Dump_Process_State(&exinfox, Message);\ - CheckProgramErrors();\ -} - -#define My__except_RoutineWithDisconnect(Message) \ -__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ -{\ - Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ - exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ - exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ - exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ - FreeSemaphore(&ChatSemaphore);\ - if (conn->BPQStream < 0)\ - CloseConsole(conn->BPQStream);\ - else\ - Disconnect(conn->BPQStream);\ -} -*/ -#define My_except_RoutineWithDiscBBS(Message) \ -__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ -{\ - Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ - exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ - exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ - exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ - if (conn->BPQStream < 0)\ - CloseConsole(conn->BPQStream);\ - else\ - Disconnect(conn->BPQStream);\ - CheckProgramErrors();\ -} -#endif -#define MAXUSERNAMELEN 6 - -#define WSA_ACCEPT WM_USER + 1 -#define WSA_CONNECT WM_USER + 2 -#define WSA_DATA WM_USER + 3 -#define NNTP_ACCEPT WM_USER + 4 -#define NNTP_DATA WM_USER + 5 - -#ifdef _DEBUG - -VOID * _malloc_dbg_trace(int len, int type, char * file, int line); - -#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define _recalloc(p, c, s) _recalloc_dbg(p, c, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) -#define free(p) _free_dbg(p, _NORMAL_BLOCK) -#define _strdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) - - -#define zalloc(s) _zalloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) -#else -#define zalloc(s) _zalloc(s) -#endif - -#ifdef LINBPQ - -#undef zalloc -#define zalloc _zalloc - -#endif - -VOID * _zalloc_dbg(size_t len, int type, char * file, int line); - -#define LOG_BBS 0 -#define LOG_CHAT 1 -#define LOG_TCP 2 -#define LOG_DEBUG_X 3 - - -//Chat Duplicate suppression Code - -#define MAXDUPS 10 // Number to keep -#define DUPSECONDS 5 // TIme to Keep - -struct DUPINFO -{ - time_t DupTime; - char DupUser[10]; - char DupText[100]; -}; - - -struct UserRec -{ - char * Callsign; - char * UserName; - char * Password; -}; - - - -typedef struct ConnectionInfo_S -{ - struct ConnectionInfo_S *next; - PROC *proc; - UCHAR RadioOnlyMode; // T or R flag for Radio Only mode. - - int Number; // Number of record - for Connections display - BOOL Active; - int BPQStream; - int paclen; - UCHAR Callsign[11]; // Station call including SSID - BOOL GotHeader; - UCHAR InputMode; // Line by Line or Binary or YAPP - - UCHAR * InputBuffer; - int InputBufferLen; - int InputLen; // Data we have already = Offset of end of an incomplete packet; - - struct UserInfo * UserPointer; - int Retries; - int LoginState; // 1 = user ok, 2 = password ok - int Flags; - - // Data to the user is kept in a malloc'd buffer. This can be appended to, - // and data sucked out under both terminal and system flow control. PACLEN is - // enfored when sending to node. - - UCHAR * OutputQueue; // Messages to user - int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message - int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. - - int CloseAfterFlush; // Close session when all sent. Set to 100ms intervals to wait. - - int ErrorCount; // Invalid Command count - BOOL Paging; // Set if user wants paging - int LinesSent; // Count when paging - int PageLen; // Lines per page - - UCHAR * MailBuffer; // Mail Message being received - UCHAR * CopyBuffer; // Mail Message being forwarded - int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct - - long lastmsg; // Last Listed. Stored here, updated in user record only on clean close - BOOL sysop; // Set if user is authenticated as a sysop - BOOL Secure_Session; // Set if Local Terminal, or Telnet connect with SYSOP status - UINT BBSFlags; // Set if defined as a bbs and SID received - struct MsgInfo * TempMsg; // Header while message is being received - struct MsgInfo * FwdMsg; // Header while message is being forwarded - - char ** To; // May be several Recipients - int ToCount; - - int BBSNumber; // The BBS number (offset into bitlist of BBSes to forward a message to - int NextMessagetoForward; // Next index to check in forward cycle - BOOL BPQBBS; // Set if SID indicates other end is BPQ - char MSGTYPES[20]; // Any MSGTYPEFLAGS - BOOL SendT; // Send T messages - BOOL SendP; // Send P messages - BOOL SendB; // Send Bulls - BOOL SendWL2KFW; // send ;FW: - int MaxBLen; // Max Size for this session - int MaxPLen; // Max Size for this session - int MaxTLen; // Max Size for this session - BOOL DoReverse; // Request Reverse Forward - char LastForwardType; // Last type of messages forwarded - struct FBBHeaderLine * FBBHeaders; // The Headers from an FFB forward block - char FBBReplyChars[36]; //The +-=!nnnn chars for the 5 proposals - int FBBReplyIndex; // current Reply Pointer - int FBBIndex; // current propopsal number - int RestartFrom; // Restart position - BOOL NeedRestartHeader; // Set if waiting for 6 byte restart header - BOOL DontSaveRestartData; // Set if corrupt data received - BOOL FBBMsgsSent; // Messages need to be maked as complete when next command received - UCHAR FBBChecksum; // Header Checksum - BOOL OpenBCM; // OpenBCM mode (escape -xFF chars) - BOOL InTelnetExcape; // Last Char was 0xff - BOOL LocalMsg; // Set if current Send command is for a local user - BOOL NewUser; // Set if first time user has accessed BBS - BOOL Paclink; // Set if receiving messages from Paclink - BOOL RMSExpress; // Set if receiving messages from RMS Express - BOOL WL2K; // Set if communicating with a CMS - BOOL PAT; // Set if communicating with PAT - char ** PacLinkCalls; // Calls we are getting messages for - BOOL SkipPrompt; // Set if a remote node sends a > at the end of his CTEXT - BOOL SkipConn; // Node sends "connected" in its CTEXT - int Watchdog; // Hung Circuit Detect. - int SessType; // BPQ32 sesstype bits - -#define Sess_L2LINK 1 -#define Sess_SESSION 2 -#define Sess_UPLINK 4 -#define Sess_DOWNLINK 8 -#define Sess_BPQHOST 0x20 -#define Sess_PACTOR 0x40 - - HANDLE DebugHandle; // File Handle for session-based debugging - - char ARQFilename[256]; // Filename from ARQ:FILE:: Header - int ARQClearCount; // To make sure queues are flushed when sending - - int SIDResponseTimer; // Used to detect incomplete handshake - - char PQChallenge[20]; // Secure User logon challange - char SecureMsg[20]; // CMS Secure Signon Response - int MCastListenTime; // Time to run session for - - int YAPPLen; // Bytes sent/received of YAPP Message - long YAPPDate; // Date for received file - if set enables YAPPC - - int SyncCompressedLen; - int SyncXMLLen; - int SyncMsgLen; - char * SyncHost; // Saved so can send "request sync" - int SyncPort; - UCHAR * SyncMessage; // Compressed SYNC message to send - - // These are used to detect CRLF split over a packet boundary - int usingCR; // Session is (normally) using CR as terminator - int lastLineEnd; // Terminator for current line - - struct ConnectionInfo_S * SysopChatStream; // Stream sysop is chatting to - -} ConnectionInfo, CIRCUIT; - -// Flags Equates - -#define GETTINGUSER 1 -#define GETTINGBBS 2 -#define CHATMODE 4 -#define GETTINGTITLE 8 -#define GETTINGMESSAGE 16 -#define CHATLINK 32 // Link to another Chat Node -#define SENDTITLE 64 -#define SENDBODY 128 -#define WAITPROMPT 256 // Waiting for prompt after message -#define PROPOSINGSYNCMSG 512 // Sent proposal to SYNC, waiting response -#define SENDINGSYNCMSG 1024 // Sent message to SYNC, waiting response -#define REQUESTINGSYNC 2048 -#define GETTINGSYNCMESSAGE 4096 // Receiving body of a SYNC message - -// BBSFlags Equates - -#define BBS 1 -#define FBBForwarding 2 -#define FBBCompressed 4 -#define FBBB1Mode 8 -#define FBBB2Mode 16 -#define RunningConnectScript 32 -#define MBLFORWARDING 64 // MBL Style Frwarding- waiting for OK/NO or Prompt following message -#define TEXTFORWARDING 128 // Plain Text forwarding -#define OUTWARDCONNECT 256 // We connected to them -#define FLARQMODE 512 // Message from FLARQ -#define FLARQMAIL 1024 // Sending FLARQ Format Message -#define ARQMAILACK 2048 // Waiting for all data to be acked -#define NEEDLF 4096 // Add LF to forward script commands (fro Telnet -#define MCASTRX 8192 // Stream in Multicast RX Mode -#define DISCONNECTING 16384 // Disconnect sent to Node -#define YAPPTX 0x008000 // Sending YAPP file -#define SYSOPCHAT 0x010000 // Chatting to BBS console -#define WINLINKRO 0x020000 // WL2K RO (no J in SID) -#define SYNCMODE 0x040000 // RMS RELAY SYNC -#define MFJMODE 0x080000 // MFJ PMS -#define NEWPACCOM 0x100000 // PACCOM PMS 3.2 -#define SETCALLTOSENDER 0x200000 // Set calling call to message sender - - -struct FBBRestartData -{ - struct MsgInfo * TempMsg; // Header while message is being received - struct UserInfo * UserPointer; - UCHAR * MailBuffer; // Mail Message being received - int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct - int Count; // Give up if too many restarts -}; - -// We need to keep the B2Message file for B2 messages we are sending until the messages is acked, so -// we can restart it. Otherwise the file may change, resulting in a checksum error - - -struct B2RestartData -{ - int CSize; // Compresses Size (B2 proto) - UCHAR * CompressedMsg; // Compressed Body fo B2 - struct MsgInfo * FwdMsg; - struct UserInfo * UserPointer; - int Count; // Give up if too many restarts -}; - -#pragma pack(1) - -struct TempUserInfo -{ - int LastAuthCode; // Protect against playback attack - - // Fields used to allow interrupting and resuming a paged listing - - BOOL ListActive; // Doing a list - BOOL ListSuspended; // Paused doing a list - int LastListedInPagedMode; - char LastListCommand[80]; - char LastListParams[80]; - int LinesSent; - char SendFullFrom; - char ListType; - char ListDirn; - char ListStatus; - char ListSelector; // < > @ etc - - int ListRangeStart; - int ListRangeEnd; - int LLCount; // Number still to send in List Last N - int UpdateLatest; // if set, save last listed as latest - BOOL IncludeKilled; // Show Killed Messages if SYSOP - -}; - -#define PMSG 1 -#define BMSG 2 -#define TMSG 3 - -struct OldUserInfo -{ - // Old format - without message type specific traffic counts - - char Call[10]; // Connected call without SSID -// indicat relai[8]; /* 64 Digis path */ - int lastmsg; /* 4 Last L number */ - int ConnectsIn; /* 4 Number of connexions in*/ - int TimeLastConnected; //Last connexion date */ -// long lastyap __a2__ ; /* 4 Last YN date */ - ULONG flags ; /* 4 Flags */ - - UCHAR PageLen; // Lines Per Page - UCHAR lang ; /* 1 Language */ - - int Xnewbanner; /* 4 Last Banner date */ - short Xdownload ; /* 2 download size (KB) = 100 */ - char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) - char BBSNumber; // BBS Bitmap Index Number - struct BBSForwardingInfo * ForwardingInfo; - struct UserInfo * BBSNext; // links BBS record - struct TempUserInfo * Temp; // Working Fields - not saved in user file - char xfree[6]; /* 6 Spare */ - char Xtheme; /* 1 Current topic */ - - char Name[18]; /* 18 1st Name */ - char Address[61]; /* 61 Address */ - - // Stats. Was City[31]; /* 31 City */ - - int MsgsReceived; - int MsgsSent; - int MsgsRejectedIn; // Messages we reject - int MsgsRejectedOut; // Messages Rejectd by other end - int BytesForwardedIn; - int BytesForwardedOut; - int ConnectsOut; // Forwarding Connects Out - - USHORT RMSSSIDBits; // SSID's to poll in RMS - - char Spare1; - - char HomeBBS[41]; /* 41 home BBS */ - char QRA[7]; /* 7 Qth Locator */ - char pass[13]; /* 13 Password */ - char ZIP[9]; /* 9 Zipcode */ - BOOL spare; -} ; /* Total : 360 bytes */ - -struct MsgStats -{ - int ConnectsIn; /* 4 Number of connexions in*/ - int ConnectsOut; // Forwarding Connects Out - - // Stats saveed by message type - - int MsgsReceived[4]; - int MsgsSent[4]; - int MsgsRejectedIn[4]; // Messages we reject - int MsgsRejectedOut[4]; // Messages Rejectd by other end - int BytesForwardedIn[4]; - int BytesForwardedOut[4]; -}; - -struct UserInfo -{ - // New Format - with stats maintained by message type and unused fields removed. - - char Call[10]; // Connected call without SSID - - int Length; // To make subsequent format changes easier - - int lastmsg; /* 4 Last L number */ - int xTimeLastConnected; //Last connexion date */ - ULONG flags ; /* 4 Flags */ - - UCHAR PageLen; // Lines Per Page - - char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) - unsigned char BBSNumber; // BBS Bitmap Index Number - struct BBSForwardingInfo * ForwardingInfo; - struct UserInfo * BBSNext; // links BBS record - struct TempUserInfo * Temp; // Working Fields - not saved in user file - char Name[18]; /* 18 1st Name */ - char Address[61]; /* 61 Address */ - - USHORT RMSSSIDBits; // SSID's to poll in RMS - - char HomeBBS[41]; /* 41 home BBS */ - char QRA[7]; /* 7 Qth Locator */ - char pass[13]; /* 13 Password */ - char ZIP[9]; /* 9 Zipcode */ - - struct MsgStats Total; - struct MsgStats Last; - - char CMSPass[16]; // For Secure Signon - int WebSeqNo; - - long long TimeLastConnected; //Last connection date */ - - char Filler[44 - 8]; // So we can add a few fields wirhout another resize -}; - -// flags equates - -#define F_Excluded 0x0001 -#define F_GGG 0x0002 -#define F_Expert 0x0004 -#define F_SYSOP 0x0008 -#define F_BBS 0x0010 -#define F_RMSREDIRECT 0x0020 -#define F_BBB 0x0040 -#define F_CCC 0x0080 -#define F_DDD 0x0100 -#define F_EEE 0x0200 -#define F_FFF 0x0400 -#define F_PMS 0x0800 -#define F_EMAIL 0x1000 -#define F_HOLDMAIL 0x2000 -#define F_POLLRMS 0x4000 -#define F_SYSOP_IN_LM 0x8000 -#define F_Temp_B2_BBS 0x00010000 // "Winlink Express User" -#define F_NOWINLINK 0x00020000 // Don't add Winlink.org -#define F_NOBULLS 0x00040000 -#define F_NTSMPS 0x00080000 -#define F_APRSMFOR 0x00100000 // Send APRS message for new mail -#define F_APRSSSID 0xF0000000 // (Top 4 Bits - - -struct Override -{ - char * Call; - int Days; -}; - -struct ALIAS -{ - char * Alias; - char * Dest; -}; - -typedef struct _MESSAGEX -{ -// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT - - struct _MESSAGEX * CHAIN; - - UCHAR PORT; - USHORT LENGTH; - - UCHAR DEST[7]; - UCHAR ORIGIN[7]; - -// MAY BE UP TO 56 BYTES OF DIGIS - - UCHAR CTL; - UCHAR PID; - UCHAR DATA[256]; - UCHAR DIGIS[56]; // Padding in case we have digis - -}MESSAGEX, *PMESSAGEX; - - -#pragma pack() - -// Message Database Entry. Designed to be compatible with FBB - -#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy -#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists. - -#pragma pack(1) - -struct OldMsgInfo -{ - char type ; - char status ; - int number ; - int length ; - int datereceived; - char bbsfrom[7] ; // ? BBS we got it from ? - char via[41] ; - char from[7] ; - char to[7] ; - char bid[13] ; - char title[61] ; - char bin; - int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp - - UCHAR B2Flags; - - char free[4]; - unsigned short nblu; - int theme ; - time_t datecreated ; - time_t datechanged ; - char fbbs[10] ; - char forw[10] ; - char emailfrom[41]; -} ; - - -struct MsgInfo -{ - char type; - char status; - int number; - int length; - int xdatereceived; - char bbsfrom[7]; // ? BBS we got it from ? - char via[41]; - char from[7]; - char to[7]; - char bid[13]; - char title[61]; - int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp - - UCHAR B2Flags; - - #define B2Msg 1 // Set if Message File is a formatted B2 message - #define Attachments 2 // Set if B2 message has attachments - #define FromPaclink 4 - #define FromCMS 8 - #define FromRMSExpress 16 - #define RadioOnlyMsg 32 // Received using call-T - #define RadioOnlyFwd 64 // Received using call-R - - int xdatecreated; - int xdatechanged; - UCHAR fbbs[NBMASK]; - UCHAR forw[NBMASK]; - char emailfrom[41]; - char Locked; // Set if selected for sending (NTS Pickup) - char Defered; // FBB response '=' received - UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP) - -// For 64 bit time_t compatibility define as long long -// (so struct is same with 32 or 64 bit time_t) - - long long datereceived; - long long datecreated; - long long datechanged; - - char Spare[61 - 24]; // For future use -} ; - -#define MSGTYPE_B 0 -#define MSGTYPE_P 1 - -#define MSGSTATUS_N 0 -#define MSGSTATUS_Y 1 -#define MSGSTATUS_F 2 -#define MSGSTATUS_K 3 -#define MSGSTATUS_H 4 -#define MSGSTATUS_D 5 -#define MSGSTATUS_$ 6 - -struct NNTPRec -{ - // Used for NNTP access to Bulls - - struct NNTPRec * Next; // Record held in chain, so can be held sorted - char NewsGroup[64]; // = Bull TO.at field - int FirstMsg; // Lowest Number - int LastMsg; // Highest Number - int Count; // Active Msgs - time_t DateCreated; // COntains Creation Date of First Bull in Group -}; - - -typedef struct { - char mode; - char BID[13]; - union - { /* array named screen */ - struct - { - unsigned short msgno; - unsigned short timestamp; - }; - CIRCUIT * conn; - } u; -} BIDRec, *BIDRecP; - - -typedef struct WPDBASE{ /* 194 bytes */ - char callsign[7]; - char name[13]; - unsigned char Type; - unsigned char changed; - unsigned short seen; - long long last_modif; - long long last_seen; - char first_homebbs[41]; - char secnd_homebbs[41]; - char first_zip[9]; - char secnd_zip[9]; - char first_qth[31]; - char secnd_qth[31]; -} WPRec, * WPRecP; - -#pragma pack() - -struct FWDBAND -{ - time_t FWDStartBand; - time_t FWDEndBand; -}; - - - -struct BBSForwardingInfo -{ - // Holds info for forwarding - - BOOL Enabled; // Forwarding Enabled - char ** ConnectScript; // Main Connect Script - char ** TempConnectScript; // Used with FWD command. - int ScriptIndex; // Next line in script - BOOL MoreLines; // Set until script is finsihed - - char ** TOCalls; // Calls in to field - char ** ATCalls; // Calls in ATBBS field - char ** HaddressesP; // Heirarchical Addresses for Personals to forward to (as stored) - char *** HADDRSP; // Heirarchical Addresses for Personals to forward to - char ** Haddresses; // Heirarchical Addresses to forward to (as stored) - char *** HADDRS; // Heirarchical Addresses to forward to - int * HADDROffet; // Elements added to complete the HR. At least n+1 must match to forward - char ** FWDTimes; // Time bands to forward - struct FWDBAND ** FWDBands; - int MsgCount; // Messages for this BBS - BOOL ReverseFlag; // Set if BBS wants to poll for reverse forwarding - BOOL Forwarding; // Forward in progress - int MaxFBBBlockSize; - BOOL AllowBlocked; // Allow FBB Blocked - BOOL AllowCompressed; // Allow FBB Compressed - BOOL AllowB1; // Enable B1 - BOOL AllowB2; // Enable B2 - BOOL SendCTRLZ; // Send Ctrl/z instead of /ex - BOOL PersonalOnly; // Only Forward Personals - BOOL SendNew; // Forward new messages immediately - int FwdInterval; - int RevFwdInterval; - int FwdTimer; - time_t LastReverseForward; - char *BBSHA; // HA of BBS - char ** BBSHAElements; // elements of HA of BBS - int ConTimeout; -// char UserCall[10]; // User we are forwarding on behalf of (Currently only for RMS) -// int UserIndex; // index of User we are forwarding on behalf of (Currently only for RMS) -}; - - -struct FBBHeaderLine -{ - // Holds the info from the (up to) 5 headers presented at the start of a Forward Block - - char Format; // Ascii or Binary - char MsgType; // P B etc - char From[7]; // Sender - char ATBBS[41]; // BBS of recipient (@ Field) - char To[7]; // Recipient - char BID[13]; - int Size; - int CSize; // Compresses Size (B2 proto) - BOOL B2Message; // Set if an FC type - UCHAR * CompressedMsg; // Compressed Body fo B2 - struct MsgInfo * FwdMsg; // Header so we can mark as complete -}; - -#define MAXSTACK 20 -//#define MAXLINE 10000 -#define INPUTLEN 512 - -#define MAXLINES 1000 -#define LINELEN 200 - -char RTFHeader[4000]; - -int RTFHddrLen; - -struct ConsoleInfo -{ - struct ConsoleInfo * next; - CIRCUIT * Console; - int BPQStream; - WNDPROC wpOrigInputProc; - HWND hConsole; - HWND hwndInput; - HWND hwndOutput; - HMENU hMenu; // handle of menu - RECT ConsoleRect; - RECT OutputRect; - - int Height, Width, LastY; - - int ClientHeight, ClientWidth; - char kbbuf[INPUTLEN]; - int kbptr; - - char * readbuff; // Malloc'ed - int readbufflen; // Current Length - char * KbdStack[MAXSTACK]; - - int StackIndex; - - BOOL Bells; - BOOL FlashOnBell; // Flash instead of Beep - BOOL StripLF; - - BOOL WarnWrap; - BOOL FlashOnConnect; - BOOL WrapInput; - BOOL CloseWindowOnBye; - - unsigned int WrapLen; - int WarnLen; - int maxlinelen; - - int PartLinePtr; - int PartLineIndex; // Listbox index of (last) incomplete line - - DWORD dwCharX; // average width of characters - DWORD dwCharY; // height of characters - DWORD dwClientX; // width of client area - DWORD dwClientY; // height of client area - DWORD dwLineLen; // line length - int nCaretPosX; // horizontal position of caret - int nCaretPosY; // vertical position of caret - - COLORREF FGColour; // Text Colour - COLORREF BGColour; // Background Colour - COLORREF DefaultColour; // Default Text Colour - - int CurrentLine; // Line we are writing to in circular buffer. - - int Index; - BOOL SendHeader; - BOOL Finished; - - char OutputScreen[MAXLINES][LINELEN]; - - int Colourvalue[MAXLINES]; - int LineLen[MAXLINES]; - - int CurrentColour; - int Thumb; - int FirstTime; - BOOL Scrolled; // Set if scrolled back - int RTFHeight; // Height of RTF control in pixels - -}; - - -struct MSESSION -{ - struct MSESSION * Next; - unsigned int Key; - char * FileName; - char * OrigTimeStamp; - unsigned char * Message; - int MessageLen; - unsigned char * BlockList; - char * ID; - int BlockSize; - int BlockCount; - int BlocksReceived; - BOOL Completed; - time_t Created; - time_t LastUpdated; - int Index; // Line in Display -}; - -VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...); -char * strlop(char * buf, char delim); -int rt_cmd(CIRCUIT *circuit, char * Buffer); -CIRCUIT *circuit_new(CIRCUIT *circuit, int flags); -VOID BBSputs(CIRCUIT * conn, char * buf); -VOID FBBputs(CIRCUIT * conn, char * buf); -void makelinks(void); -VOID * _zalloc(size_t len); -VOID FreeChatMemory(); -VOID ChatTimer(); -VOID nputs(CIRCUIT * conn, char * buf); -VOID node_close(); -VOID removelinks(); -VOID SetupChat(); -VOID SendChatLinkStatus(); -VOID ClearChatLinkStatus(); -VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len); - -#define Connect(stream) SessionControl(stream,1,0) -#define Disconnect(stream) SessionControl(stream,2,0) -#define ReturntoNode(stream) SessionControl(stream,3,0) -#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) - -int EncryptPass(char * Pass, char * Encrypt); -VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); - -// TCP Connections. FOr the moment SMTP or POP3 - -typedef struct SocketConnectionInfo -{ - struct SocketConnectionInfo * Next; - int Number; // Number of record - for Connections display - SOCKET socket; - SOCKADDR_IN sin; - int Type; // SMTP or POP3 - BOOL AMPR; // Set if sending to an AMPR.ORG server - char FromDomain[50]; // Domain we are sending from - struct UserInfo * bbs; // BBS dor forwarding to AMPR - int State; // Transaction State Machine - UCHAR CallSign[10]; - UCHAR TCPBuffer[3000]; // For converting byte stream to messages - int InputLen; // Data we have alreasdy = Offset of end of an incomplete packet; - - char * MailFrom; // Envelope Sender and Receiver - char ** RecpTo; // May be several Recipients - int Recipients; - - UCHAR * MailBuffer; // Mail Message being received. malloc'ed as needed - int MailBufferSize; // Total Malloc'ed size. Actual size is in MailSize - int MailSize; - int Flags; - - struct UserInfo * POP3User; - struct MsgInfo ** POP3Msgs; // Header List of messages for this uaer - int POP3MsgCount; // No of Messages - int POP3MsgNum; // Sequence number of message being received - - struct MsgInfo * SMTPMsg; // message for this SMTP connection - - UCHAR * SendBuffer; // Message being sent if socket is busy. malloc'ed as needed - int SendBufferSize; // Total Malloc'ed size. Actual size is in MailSize - int SendSize; // Bytes in buffer - int SendPtr; // next byte to send when ready - - struct NNTPRec * NNTPGroup; // Currently Selected Group - int NNTPNum; // Currenrly Selected Msg Number - int Timeout; // Used to close a session that is open too long - -} SocketConn; - -typedef struct KEYVALUES -{ - char * Key; - char * Value; -} KeyValues; - -typedef struct WEBMAILINFO -{ - // Info for HTML Forms Processing - - struct HtmlFormDir * Dir; // HTML Directory - char * txtFileName; // Template Name for current message - char * InputHTMLName; // Template to input message - char * DisplayHTMLName; // Template to display message - char * ReplyHTMLName; // Template for replying to message - char * txtFile; // Template data - char * OrigTo; // To field when template loaded - char * OrigSubject; // Subject field when template loaded - char * OrigBody; // Msg text when template loaded - char * OrigBID; - char OrigType; - char * To; - char * CC; - char * Subject; - char * Body; - char * BID; - char Type; - struct MsgInfo * Msg; // Msg record if replying - KeyValues txtKeys[1000]; // Key/Value pairs for txt template. Used when creating or displaying - KeyValues XMLKeys[1000]; // Key/Value pairs from XML attachment - BOOL isReply; - char * XMLName; - char * XML; // XML attachment - int XMLLen; - int Files; - char * FileName[100]; // Attachments - char * FileBody[100]; - int FileLen[100]; - - char * Header; - int HeaderLen; - - char * Footer; - int FooterLen; - - char * Reply; // put in here to save passing lots of parameters - int * RLen; - - BOOL Winlink; - BOOL P2P; - BOOL Packet; - - int CurrentMessageIndex; // Index of message currently displayed (for Prev and Next) - -}WebMailInfo; - -#define SMTPServer 1 -#define POP3SLAVE 2 -#define SMTPClient 3 -#define POP3Client 4 -#define NNTPServer 5 - -// State Values - -#define GettingUser 1 -#define GettingPass 2 -#define Authenticated 4 - -#define Connecting 8 - -// SMTP Master - -#define WaitingForGreeting 16 -#define WaitingForHELOResponse 32 -#define WaitingForFROMResponse 64 -#define WaitingForTOResponse 128 -#define WaitingForDATAResponse 256 -#define WaitingForBodyResponse 512 -#define WaitingForAUTHResponse 1024 - -// POP3 Master - -#define WaitingForUSERResponse 32 -#define WaitingForPASSResponse 64 -#define WaitingForSTATResponse 128 -#define WaitingForUIDLResponse 256 -#define WaitingForLISTResponse 512 -#define WaitingForRETRResponse 512 -#define WaitingForDELEResponse 1024 -#define WaitingForQUITResponse 2048 - - -#define SE 240 // End of subnegotiation parameters -#define NOP 241 //No operation -//#define DM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. -#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. -#define IP 244 //Suspend Interrupt or abort the process to which the NVT is connected. -#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. -#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. -#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. -#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. -#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. -#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. -#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. -#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. -#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. -#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. -#define IAC 255 - -#define suppressgoahead 3 //858 -//#define Status 5 //859 -//#define echo 1 //857 -#define timingmark 6 //860 -#define terminaltype 24 //1091 -#define windowsize 31 //1073 -#define terminalspeed 32 //1079 -#define remoteflowcontrol 33 //1372 -#define linemode 34 //1184 -#define environmentvariables 36 //1408 - -BOOL Initialise(); -#ifdef WIN32 -INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -#endif -int DisplaySessions(); -int DoStateChange(int Stream); -int DoReceivedData(int Stream); -int DoBBSMonitorData(int Stream); -int Connected(int Stream); -int Disconnected(int Stream); -//int Socket_Accept(int SocketId); -//int Socket_Data(int SocketId,int error, int eventcode); -int DataSocket_Read(SocketConn * sockptr, SOCKET sock); -int DataSocket_Write(SocketConn * sockptr, SOCKET sock); -int DataSocket_Disconnect(SocketConn * sockptr); -int RefreshMainWindow(); -int Terminate(); -int SendtoSocket(SOCKET sock,char * Msg); -int WriteLog(char * msg); -int ConnectState(int Stream); -UCHAR * EncodeCall(UCHAR * Call); -int ParseIniFile(char * fn); -struct UserInfo * AllocateUserRecord(char * Call); -struct MsgInfo * AllocateMsgRecord(); -BIDRec * AllocateBIDRecord(); -BIDRec * AllocateTempBIDRecord(); -struct UserInfo * LookupCall(char * Call); -BIDRec * LookupBID(char * BID); -BIDRec * LookupTempBID(char * BID); -VOID RemoveTempBIDS(CIRCUIT * conn); -VOID SaveUserDatabase(); -VOID GetUserDatabase(); -VOID GetMessageDatabase(); -VOID SaveMessageDatabase(); -VOID GetBIDDatabase(); -VOID SaveBIDDatabase(); -VOID GetWPDatabase(); -VOID CopyWPDatabase(); -VOID SaveWPDatabase(); -VOID GetBadWordFile(); -WPRec * LookupWP(char * Call); -VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user); -VOID ProcessLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); -VOID ProcessChatLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); -VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user); -int QueueMsg( ConnectionInfo * conn, char * msg, int len); -VOID SendUnbuffered(int stream, char * msg, int len); -//int GetFileList(char * Dir); -BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp); -void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); -void DoKillCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); -void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context); -void DoReadCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); -void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); -int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call); -int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call); -void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); - -VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB); -int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); -int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); -int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); -void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); -void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); -int GetUserMsg(int m, char * Call, BOOL SYSOP); -void Flush(ConnectionInfo * conn); -VOID ClearQueue(ConnectionInfo * conn); -void TrytoSend(); -void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); -struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop); -char * ReadMessageFile(int msgno); -char * ReadInfoFile(char * File); -char * FormatDateAndTime(time_t Datim, BOOL DateOnly); -int CriticalErrorHandler(char * error); -BOOL DoSendCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); -BOOL CreateMessage(ConnectionInfo * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title); -VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); -VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); -VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg); -int ProcessConnecting(CIRCUIT * circuit, char * Buffer, int Len); -VOID SaveConfig(char * ConfigName); -BOOL GetConfig(char * ConfigName); -int GetIntValue(config_setting_t * group, char * name); -BOOL GetStringValue(config_setting_t * group, char * name, char * value); -BOOL GetConfigFromRegistry(); -VOID Parse_SID(CIRCUIT * conn, char * SID, int len); -VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len); -VOID ProcessFBBLine(ConnectionInfo * conn, struct UserInfo * user, UCHAR * Buffer, int len); -VOID SetupNextFBBMessage(CIRCUIT * conn); -BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); -int PrintMessages(HWND hDlg, int Count, int * Indexes); -int check_fwd_bit(char *mask, int bbsnumber); -void set_fwd_bit(char *mask, int bbsnumber); -void clear_fwd_bit (char *mask, int bbsnumber); -VOID SetupForwardingStruct(struct UserInfo * user); -BOOL Forward_Message(struct UserInfo * user, struct MsgInfo * Msg); -VOID StartForwarding (int BBSNumber, char ** TempScript); -BOOL Reverse_Forward(); -int ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len); -BOOL FBBDoForward(CIRCUIT * conn); -BOOL FindMessagestoForward(CIRCUIT * conn); -BOOL SeeifMessagestoForward(int BBSNumber, CIRCUIT * Conn); -int CountMessagestoForward(struct UserInfo * user); - -VOID * GetMultiLineDialogParam(HWND hDialog, int DLGItem); - -#define LIBCONFIG_STATIC -#include "libconfig.h" -VOID * GetMultiStringValue(config_setting_t * hKey, char * ValueName); -VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName); - -int MultiLineDialogToREG_MULTI_SZ(HWND hWnd, int DLGItem, HKEY hKey, char * ValueName); -int Do_BBS_Sel_Changed(HWND hDlg); -VOID FreeForwardingStruct(struct UserInfo * user); -VOID FreeList(char ** Hddr); -int Do_User_Sel_Changed(HWND hDlg); -int Do_Msg_Sel_Changed(HWND hDlg); -VOID Do_Save_Msg(); -VOID Do_Add_User(HWND hDlg); -VOID Do_Delete_User(HWND hDlg); -VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user); -VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user); -VOID Do_Save_User(HWND hDlg, BOOL ShowBox); -VOID DeleteBBS(); -VOID AddBBS(); -VOID SaveBBSConfig(); -BOOL GetChatConfig(); -VOID SaveChatConfig(); -VOID SaveISPConfig(); -VOID SaveFWDConfig(); -VOID SaveMAINTConfig(); -VOID SaveWelcomeMsgs(); -VOID SavePrompts(); -VOID ReinitializeFWDStruct(struct UserInfo * user); -VOID CopyBIDDatabase(); -VOID CopyMessageDatabase(); -VOID CopyUserDatabase(); -VOID FWDTimerProc(); -VOID CreateMessageFromBuffer(CIRCUIT * conn); -VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...); -VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...); -VOID FreeOverrides(); -VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length); -struct UserInfo * FindRMS(); -VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo); -BOOL ConnecttoBBS (struct UserInfo * user); -BOOL SetupNewBBS(struct UserInfo * user); -VOID CreateRegBackup(); -VOID SaveFilters(HWND hDlg); -BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type); -BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID); -BOOL CheckifLocalRMSUser(char * FullTo); -VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Context); -BOOL wildcardcompare(char * Target, char * Match); -VOID SendWarningToSYSOP(struct MsgInfo * Msg); -VOID DoEditUserCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoPollRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoShowRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoSetIdleTime(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID DoFwdCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID SaveFwdParams(char * Call, struct BBSForwardingInfo * ForwardingInfo); -VOID DoAuthCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); -VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); -VOID DoReroute(CIRCUIT * conn, struct UserInfo * user); - -// FBB Routines - -VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg); -VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); -VOID UnpackFBBBinary(CIRCUIT * conn); -void Decode(CIRCUIT * conn, __int16 DecodeOnly); -//long Encode(char * in, char * out, long inlen, BOOL B1Protocol); - -BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline); -VOID SaveFBBBinary(CIRCUIT * conn); -BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); -BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); - -// Console Routines - -BOOL CreateConsole(int Stream); -int WritetoConsoleWindow(int Stream, char * Msg, int len); -int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item); -void CopyRichTextToClipboard(HWND hWnd); -void CopyToClipboard(HWND hWnd); -VOID CloseConsole(int Stream); - -// Monitor Routines - -BOOL CreateMonitor(); -int WritetoMonitorWindow(char * Msg, int len); - -BOOL CreateDebugWindow(); -VOID WritetoDebugWindow(char * Msg, int len); -VOID ClearDebugWindow(); -int RemoveLF(char * Message, int len); - -// Utilities - -BOOL isdigits(char * string); -void GetSemaphore(struct SEM * Semaphore, int ID); -void FreeSemaphore(struct SEM * Semaphore); - -VOID __cdecl Debugprintf(const char * format, ...); -VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...); - -VOID SortBBSChain(); -VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG); -int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup); - -// TCP Routines - -BOOL InitialiseTCP(); -VOID SetupListenSet(); -VOID TCPTimer(); -VOID TCPFastTimer(); -int Socket_Data(int sock, int error, int eventcode); -static int Socket_Accept(SOCKET SocketId); -int Socket_Connect(SOCKET sock, int Error); -VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len); -int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int Msglen, BOOL B2Flag); -BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg); -SOCKET CreateListeningSocket(int Port); -int TidyString(char * MailFrom); -VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len); -char *str_base64_encode(char *str); -int b64decode(char *str); -SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody); -BOOL POP3Connect(char * Host, int Port); -VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len); -VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len); -int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag); -void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); -int SendSock(SocketConn * sockptr, char * msg); -VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...); -VOID SendFromQueue(SocketConn * sockptr); -VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes); -int CountMessagesTo(struct UserInfo * user, int * Unread); - -BOOL SendtoISP(); - -// NNTP ROutines - -VOID InitialiseNNTP(); -VOID BuildNNTPList(struct MsgInfo * Msg); -int NNTP_Data(int sock, int error, int eventcode); -int NNTP_Accept(SOCKET SocketId); - -VOID * GetOverrides(config_setting_t * group, char * ValueName); -VOID * RegGetOverrides(HKEY hKey, char * ValueName); - -VOID DoHouseKeeping(BOOL Mainual); -VOID ExpireMessages(); -VOID KillMsg(struct MsgInfo * Msg); -BOOL RemoveKilledMessages(); -VOID Renumber_Messages(); -BOOL ExpireBIDs(); -VOID MailHousekeepingResults(); -VOID CreateBBSTrafficReport(); -VOID CreateWPReport(); - -// WP Routines - -VOID ProcessWPMsg(char * MailBuffer, int Size, char * FisrtRLine); -VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime); -VOID UpdateWPWithUserInfo(struct UserInfo * user); -VOID GetWPBBSInfo(char * Rline); - -// UI Routines - -VOID SetupUIInterface(); -VOID Free_UI(); -VOID SendLatestUI(int Port); -VOID SendMsgUI(struct MsgInfo * Msg); -static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue); -VOID SeeifBBSUIFrame(struct _MESSAGEX * buff, int len); -struct MsgInfo * FindMessageByNumber(int msgno); -int CountConnectionsOnPort(int CheckPort); - -// Message Routing Routtines - -VOID SetupHAElements(struct BBSForwardingInfo * ForwardingInfo); -VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo); -VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo); -VOID SetupMyHA(); -VOID SetupFwdAliases(); -struct Continent * FindContinent(char * Name); -int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn); -BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); -BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo); -BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); -BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); -BOOL CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); -BOOL CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); -int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo); -int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); - -VOID ReRouteMessages(); - -VOID initUTF8(); -int Is8Bit(unsigned char *cpt, int len); -int IsUTF8(unsigned char *ptr, int len); -int IsUTF8(unsigned char *ptr, int len); -int WebIsUTF8(unsigned char *ptr, int len); -int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); -int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); -int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); -int TrytoGuessCode(unsigned char * Char, int Len); - - -VOID FreeWebMailMallocs(); - -extern int _MYTIMEZONE; - -extern HKEY REGTREE; -extern char * REGTREETEXT; - -extern HBRUSH bgBrush; -extern BOOL cfgMinToTray; - -extern CIRCUIT * Console; - -extern ULONG ChatApplMask; -extern char Verstring[]; - -extern char SignoffMsg[]; -extern char AbortedMsg[]; -extern char InfoBoxText[]; // Text to display in Config Info Popup - -extern int LastVer[4]; // In case we need to do somthing the first time a version is run - -extern HWND MainWnd; -extern char BaseDir[]; -extern char BaseDirRaw[]; -extern char MailDir[]; -extern char WPDatabasePath[]; -extern char RlineVer[50]; - -extern BOOL LogBBS; -extern BOOL LogCHAT; -extern BOOL LogTCP; -extern BOOL ForwardToMe; -extern BOOL OnlyKnown; - -extern BOOL AllowAnon; -extern BOOL UserCantKillT; -extern BOOL DontNeedHomeBBS; - -extern int LatestMsg; -extern char BBSName[]; -extern char SYSOPCall[]; -extern char BBSSID[]; -extern char NewUserPrompt[]; - -extern char * WelcomeMsg; -extern char * NewWelcomeMsg; -extern char * ChatWelcomeMsg; -extern char * NewChatWelcomeMsg; -extern char * ExpertWelcomeMsg; - -extern char * Prompt; -extern char * NewPrompt; -extern char * ExpertPrompt; - -// Filter Params - -extern char ** RejFrom; // Reject on FROM Call -extern char ** RejTo; // Reject on TO Call -extern char ** RejAt; // Reject on AT Call -extern char ** RejBID; - -extern char ** HoldFrom; // Hold on FROM Call -extern char ** HoldTo; // Hold on TO Call -extern char ** HoldAt; // Hold on AT Call -extern char ** HoldBID; - -// Send WP Params - -extern BOOL SendWP; -extern char SendWPVIA[81]; -extern char SendWPTO[11]; -extern int SendWPType; - -extern int Ver[4]; - -extern struct MsgInfo ** MsgHddrPtr; - -extern BIDRec ** BIDRecPtr; -extern int NumberofBIDs; - -extern struct NNTPRec * FirstNNTPRec; -//extern int NumberofNNTPRecs; - - -extern int NumberofMessages; -extern int FirstMessageIndextoForward; - -extern WPRec ** WPRecPtr; -extern int NumberofWPrecs; - -extern struct SEM AllocSemaphore; -extern struct SEM ConSemaphore; -extern struct SEM MsgNoSemaphore; - -extern struct MsgInfo * MsgnotoMsg[]; // Message Number to Message Slot List. - - -extern char hostname[]; -extern char RtUsr[]; -extern char RtUsrTemp[]; -extern char RtKnown[]; -extern int AXIPPort; -extern BOOL NeedStatus; - -extern BOOL ISP_Gateway_Enabled; -extern BOOL SMTPAuthNeeded; - - -extern int MaxMsgno; -extern int BidLifetime; -extern int MaxAge; -extern int MaintInterval; -extern int MaintTime; -extern int UserLifetime; - -extern int MaxRXSize; -extern int MaxTXSize; - -extern char OurNode[]; -extern char OurAlias[]; -extern BOOL SMTPMsgCreated; - -extern HINSTANCE hInst; -extern HWND hWnd; -extern RECT MainRect; - -extern char BBSName[]; -extern char HRoute[]; -extern char AMPRDomain[]; -extern BOOL SendAMPRDirect; -extern int BBSApplNum; -extern int SMTPInPort; -extern int POP3InPort; -extern int NNTPInPort; -extern BOOL RemoteEmail; - -extern int MaxStreams; -extern UCHAR * OtherNodes; -extern struct UserInfo * BBSChain; // Chain of users that are BBSes -extern struct UserInfo ** UserRecPtr; -extern int NumberofUsers; -extern struct MsgInfo ** MsgHddrPtr; -extern int NumberofMessages; -extern int HighestBBSNumber; -extern HMENU hFWDMenu; // Forward Menu Handle -extern char zeros[]; // For forward bitmask tests -extern BOOL EnableUI; -extern BOOL RefuseBulls; -extern BOOL SendSYStoSYSOPCall; -extern BOOL SendBBStoSYSOPCall; -extern BOOL DontHoldNewUsers; -extern BOOL DefaultNoWINLINK; -extern BOOL UIEnabled[]; -extern BOOL UINull[]; -extern BOOL UIMF[]; -extern BOOL UIHDDR[]; -extern char * UIDigi[]; -extern int MailForInterval; -extern char MailForText[]; - -extern BOOL ISP_Gateway_Enabled; - -extern char MyDomain[]; // Mail domain for BBS<>Internet Mapping - -extern char ISPSMTPName[]; -extern char ISPEHLOName[]; -extern int ISPSMTPPort; - -extern char ISPPOP3Name[]; -extern int ISPPOP3Port; -extern int ISPPOP3Interval; - -extern char ISPAccountName[]; -extern char ISPAccountPass[]; -extern char EncryptedISPAccountPass[]; -extern int EncryptedPassLen; -extern char *month[]; - -extern HWND hDebug; -extern RECT MonitorRect; -extern RECT DebugRect; -extern HWND hMonitor; -//extern HWND hConsole; -//extern RECT ConsoleRect; -extern int LogAge; -extern BOOL DeletetoRecycleBin; -extern BOOL SuppressMaintEmail; -extern BOOL SaveRegDuringMaint; -extern BOOL SendWP; -extern BOOL OverrideUnsent; -extern BOOL SendNonDeliveryMsgs; -extern BOOL GenerateTrafficReport; - -extern double PR; -extern double PUR; -extern double PF; -extern double PNF; -extern int BF; -extern int BNF; -extern int NTSD; -extern int NTSU; -extern int NTSF; -extern int AP; -extern int AB; - -extern struct Override ** LTFROM; -extern struct Override ** LTTO; -extern struct Override ** LTAT; - -extern time_t LastHouseKeepingTime; -extern time_t LastTrafficTime; - -extern char * MyElements[]; -extern char ** AliasText; -extern struct ALIAS ** Aliases; - -extern BOOL ReaddressLocal; -extern BOOL ReaddressReceived; -extern BOOL WarnNoRoute; -extern BOOL SendPtoMultiple; -extern BOOL Localtime; - -struct ConsoleInfo * ConsHeader[2]; - -extern BOOL NeedHomeBBS; -extern char ConfigName[250]; -extern BOOL UsingingRegConfig; - -extern BOOL MulticastRX; - -extern BOOL FilterWPBulls; -extern BOOL NoWPGuesses; -extern char ** SendWPAddrs; // Replacers WP To and VIA - -extern BOOL DontCheckFromCall; - -// YAPP stuff - -#define SOH 1 -#define STX 2 -#define ETX 3 -#define EOT 4 -#define ENQ 5 -#define ACK 6 -#define DLE 0x10 -#define NAK 0x15 -#define CAN 0x18 +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#define LIBCONFIG_STATIC +#include + +#include "compatbits.h" + +#ifndef LINBPQ +#include "bpq32.h" +#include "BPQMailrc.h" +#include "dbghelp.h" +#else +#include "CHeaders.h" +#endif + +#include "asmstrucs.h" + + +#define NEWROUTING + + + +// Standard __except handler for try/except + +VOID CheckProgramErrors(); +VOID WriteMiniDump(); + +extern int ProgramErrors; + +extern struct _EXCEPTION_POINTERS exinfox; + +#ifdef WIN32 +Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg); + +#define My__except_Routine(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + CheckProgramErrors();\ + WriteMiniDump();\ +} + + +/* +#define My__except_Routine(Message) \ +__except(memcpy(&exinfox, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Dump_Process_State(&exinfox, Message);\ + CheckProgramErrors();\ +} + +#define My__except_RoutineWithDisconnect(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + FreeSemaphore(&ChatSemaphore);\ + if (conn->BPQStream < 0)\ + CloseConsole(conn->BPQStream);\ + else\ + Disconnect(conn->BPQStream);\ +} +*/ +#define My_except_RoutineWithDiscBBS(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + if (conn->BPQStream < 0)\ + CloseConsole(conn->BPQStream);\ + else\ + Disconnect(conn->BPQStream);\ + CheckProgramErrors();\ +} +#endif +#define MAXUSERNAMELEN 6 + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_CONNECT WM_USER + 2 +#define WSA_DATA WM_USER + 3 +#define NNTP_ACCEPT WM_USER + 4 +#define NNTP_DATA WM_USER + 5 + +#ifdef _DEBUG + +VOID * _malloc_dbg_trace(int len, int type, char * file, int line); + +#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _recalloc(p, c, s) _recalloc_dbg(p, c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define free(p) _free_dbg(p, _NORMAL_BLOCK) +#define _strdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) + + +#define zalloc(s) _zalloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#else +#define zalloc(s) _zalloc(s) +#endif + +#ifdef LINBPQ + +#undef zalloc +#define zalloc _zalloc + +#endif + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line); + +#define LOG_BBS 0 +#define LOG_CHAT 1 +#define LOG_TCP 2 +#define LOG_DEBUG_X 3 + + +//Chat Duplicate suppression Code + +#define MAXDUPS 10 // Number to keep +#define DUPSECONDS 5 // TIme to Keep + +struct DUPINFO +{ + time_t DupTime; + char DupUser[10]; + char DupText[100]; +}; + + +struct UserRec +{ + char * Callsign; + char * UserName; + char * Password; +}; + + + +typedef struct ConnectionInfo_S +{ + struct ConnectionInfo_S *next; + PROC *proc; + UCHAR RadioOnlyMode; // T or R flag for Radio Only mode. + + int Number; // Number of record - for Connections display + BOOL Active; + int BPQStream; + int paclen; + UCHAR Callsign[11]; // Station call including SSID + BOOL GotHeader; + UCHAR InputMode; // Line by Line or Binary or YAPP + + UCHAR * InputBuffer; + int InputBufferLen; + int InputLen; // Data we have already = Offset of end of an incomplete packet; + + struct UserInfo * UserPointer; + int Retries; + int LoginState; // 1 = user ok, 2 = password ok + int Flags; + + // Data to the user is kept in a malloc'd buffer. This can be appended to, + // and data sucked out under both terminal and system flow control. PACLEN is + // enfored when sending to node. + + UCHAR * OutputQueue; // Messages to user + int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. + + int CloseAfterFlush; // Close session when all sent. Set to 100ms intervals to wait. + + int ErrorCount; // Invalid Command count + BOOL Paging; // Set if user wants paging + int LinesSent; // Count when paging + int PageLen; // Lines per page + + UCHAR * MailBuffer; // Mail Message being received + UCHAR * CopyBuffer; // Mail Message being forwarded + int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct + + long lastmsg; // Last Listed. Stored here, updated in user record only on clean close + BOOL sysop; // Set if user is authenticated as a sysop + BOOL Secure_Session; // Set if Local Terminal, or Telnet connect with SYSOP status + UINT BBSFlags; // Set if defined as a bbs and SID received + struct MsgInfo * TempMsg; // Header while message is being received + struct MsgInfo * FwdMsg; // Header while message is being forwarded + + char ** To; // May be several Recipients + int ToCount; + + int BBSNumber; // The BBS number (offset into bitlist of BBSes to forward a message to + int NextMessagetoForward; // Next index to check in forward cycle + BOOL BPQBBS; // Set if SID indicates other end is BPQ + char MSGTYPES[20]; // Any MSGTYPEFLAGS + BOOL SendT; // Send T messages + BOOL SendP; // Send P messages + BOOL SendB; // Send Bulls + BOOL SendWL2KFW; // send ;FW: + int MaxBLen; // Max Size for this session + int MaxPLen; // Max Size for this session + int MaxTLen; // Max Size for this session + BOOL DoReverse; // Request Reverse Forward + char LastForwardType; // Last type of messages forwarded + struct FBBHeaderLine * FBBHeaders; // The Headers from an FFB forward block + char FBBReplyChars[36]; //The +-=!nnnn chars for the 5 proposals + int FBBReplyIndex; // current Reply Pointer + int FBBIndex; // current propopsal number + int RestartFrom; // Restart position + BOOL NeedRestartHeader; // Set if waiting for 6 byte restart header + BOOL DontSaveRestartData; // Set if corrupt data received + BOOL FBBMsgsSent; // Messages need to be maked as complete when next command received + UCHAR FBBChecksum; // Header Checksum + BOOL OpenBCM; // OpenBCM mode (escape -xFF chars) + BOOL InTelnetExcape; // Last Char was 0xff + BOOL LocalMsg; // Set if current Send command is for a local user + BOOL NewUser; // Set if first time user has accessed BBS + BOOL Paclink; // Set if receiving messages from Paclink + BOOL RMSExpress; // Set if receiving messages from RMS Express + BOOL WL2K; // Set if communicating with a CMS + BOOL PAT; // Set if communicating with PAT + char ** PacLinkCalls; // Calls we are getting messages for + BOOL SkipPrompt; // Set if a remote node sends a > at the end of his CTEXT + BOOL SkipConn; // Node sends "connected" in its CTEXT + int Watchdog; // Hung Circuit Detect. + int SessType; // BPQ32 sesstype bits + +#define Sess_L2LINK 1 +#define Sess_SESSION 2 +#define Sess_UPLINK 4 +#define Sess_DOWNLINK 8 +#define Sess_BPQHOST 0x20 +#define Sess_PACTOR 0x40 + + HANDLE DebugHandle; // File Handle for session-based debugging + + char ARQFilename[256]; // Filename from ARQ:FILE:: Header + int ARQClearCount; // To make sure queues are flushed when sending + + int SIDResponseTimer; // Used to detect incomplete handshake + + char PQChallenge[20]; // Secure User logon challange + char SecureMsg[20]; // CMS Secure Signon Response + int MCastListenTime; // Time to run session for + + int YAPPLen; // Bytes sent/received of YAPP Message + long YAPPDate; // Date for received file - if set enables YAPPC + + int SyncCompressedLen; + int SyncXMLLen; + int SyncMsgLen; + char * SyncHost; // Saved so can send "request sync" + int SyncPort; + UCHAR * SyncMessage; // Compressed SYNC message to send + + // These are used to detect CRLF split over a packet boundary + int usingCR; // Session is (normally) using CR as terminator + int lastLineEnd; // Terminator for current line + + struct ConnectionInfo_S * SysopChatStream; // Stream sysop is chatting to + +} ConnectionInfo, CIRCUIT; + +// Flags Equates + +#define GETTINGUSER 1 +#define GETTINGBBS 2 +#define CHATMODE 4 +#define GETTINGTITLE 8 +#define GETTINGMESSAGE 16 +#define CHATLINK 32 // Link to another Chat Node +#define SENDTITLE 64 +#define SENDBODY 128 +#define WAITPROMPT 256 // Waiting for prompt after message +#define PROPOSINGSYNCMSG 512 // Sent proposal to SYNC, waiting response +#define SENDINGSYNCMSG 1024 // Sent message to SYNC, waiting response +#define REQUESTINGSYNC 2048 +#define GETTINGSYNCMESSAGE 4096 // Receiving body of a SYNC message + +// BBSFlags Equates + +#define BBS 1 +#define FBBForwarding 2 +#define FBBCompressed 4 +#define FBBB1Mode 8 +#define FBBB2Mode 16 +#define RunningConnectScript 32 +#define MBLFORWARDING 64 // MBL Style Frwarding- waiting for OK/NO or Prompt following message +#define TEXTFORWARDING 128 // Plain Text forwarding +#define OUTWARDCONNECT 256 // We connected to them +#define FLARQMODE 512 // Message from FLARQ +#define FLARQMAIL 1024 // Sending FLARQ Format Message +#define ARQMAILACK 2048 // Waiting for all data to be acked +#define NEEDLF 4096 // Add LF to forward script commands (fro Telnet +#define MCASTRX 8192 // Stream in Multicast RX Mode +#define DISCONNECTING 16384 // Disconnect sent to Node +#define YAPPTX 0x008000 // Sending YAPP file +#define SYSOPCHAT 0x010000 // Chatting to BBS console +#define WINLINKRO 0x020000 // WL2K RO (no J in SID) +#define SYNCMODE 0x040000 // RMS RELAY SYNC +#define MFJMODE 0x080000 // MFJ PMS +#define NEWPACCOM 0x100000 // PACCOM PMS 3.2 +#define SETCALLTOSENDER 0x200000 // Set calling call to message sender + + +struct FBBRestartData +{ + struct MsgInfo * TempMsg; // Header while message is being received + struct UserInfo * UserPointer; + UCHAR * MailBuffer; // Mail Message being received + int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct + int Count; // Give up if too many restarts +}; + +// We need to keep the B2Message file for B2 messages we are sending until the messages is acked, so +// we can restart it. Otherwise the file may change, resulting in a checksum error + + +struct B2RestartData +{ + int CSize; // Compresses Size (B2 proto) + UCHAR * CompressedMsg; // Compressed Body fo B2 + struct MsgInfo * FwdMsg; + struct UserInfo * UserPointer; + int Count; // Give up if too many restarts +}; + +#pragma pack(1) + +struct TempUserInfo +{ + int LastAuthCode; // Protect against playback attack + + // Fields used to allow interrupting and resuming a paged listing + + BOOL ListActive; // Doing a list + BOOL ListSuspended; // Paused doing a list + int LastListedInPagedMode; + char LastListCommand[80]; + char LastListParams[80]; + int LinesSent; + char SendFullFrom; + char ListType; + char ListDirn; + char ListStatus; + char ListSelector; // < > @ etc + + int ListRangeStart; + int ListRangeEnd; + int LLCount; // Number still to send in List Last N + int UpdateLatest; // if set, save last listed as latest + BOOL IncludeKilled; // Show Killed Messages if SYSOP + +}; + +#define PMSG 1 +#define BMSG 2 +#define TMSG 3 + +struct OldUserInfo +{ + // Old format - without message type specific traffic counts + + char Call[10]; // Connected call without SSID +// indicat relai[8]; /* 64 Digis path */ + int lastmsg; /* 4 Last L number */ + int ConnectsIn; /* 4 Number of connexions in*/ + int TimeLastConnected; //Last connexion date */ +// long lastyap __a2__ ; /* 4 Last YN date */ + ULONG flags ; /* 4 Flags */ + + UCHAR PageLen; // Lines Per Page + UCHAR lang ; /* 1 Language */ + + int Xnewbanner; /* 4 Last Banner date */ + short Xdownload ; /* 2 download size (KB) = 100 */ + char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) + char BBSNumber; // BBS Bitmap Index Number + struct BBSForwardingInfo * ForwardingInfo; + struct UserInfo * BBSNext; // links BBS record + struct TempUserInfo * Temp; // Working Fields - not saved in user file + char xfree[6]; /* 6 Spare */ + char Xtheme; /* 1 Current topic */ + + char Name[18]; /* 18 1st Name */ + char Address[61]; /* 61 Address */ + + // Stats. Was City[31]; /* 31 City */ + + int MsgsReceived; + int MsgsSent; + int MsgsRejectedIn; // Messages we reject + int MsgsRejectedOut; // Messages Rejectd by other end + int BytesForwardedIn; + int BytesForwardedOut; + int ConnectsOut; // Forwarding Connects Out + + USHORT RMSSSIDBits; // SSID's to poll in RMS + + char Spare1; + + char HomeBBS[41]; /* 41 home BBS */ + char QRA[7]; /* 7 Qth Locator */ + char pass[13]; /* 13 Password */ + char ZIP[9]; /* 9 Zipcode */ + BOOL spare; +} ; /* Total : 360 bytes */ + +struct MsgStats +{ + int ConnectsIn; /* 4 Number of connexions in*/ + int ConnectsOut; // Forwarding Connects Out + + // Stats saveed by message type + + int MsgsReceived[4]; + int MsgsSent[4]; + int MsgsRejectedIn[4]; // Messages we reject + int MsgsRejectedOut[4]; // Messages Rejectd by other end + int BytesForwardedIn[4]; + int BytesForwardedOut[4]; +}; + +struct UserInfo +{ + // New Format - with stats maintained by message type and unused fields removed. + + char Call[10]; // Connected call without SSID + + int Length; // To make subsequent format changes easier + + int lastmsg; /* 4 Last L number */ + int xTimeLastConnected; //Last connexion date */ + ULONG flags ; /* 4 Flags */ + + UCHAR PageLen; // Lines Per Page + + char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) + unsigned char BBSNumber; // BBS Bitmap Index Number + struct BBSForwardingInfo * ForwardingInfo; + struct UserInfo * BBSNext; // links BBS record + struct TempUserInfo * Temp; // Working Fields - not saved in user file + char Name[18]; /* 18 1st Name */ + char Address[61]; /* 61 Address */ + + USHORT RMSSSIDBits; // SSID's to poll in RMS + + char HomeBBS[41]; /* 41 home BBS */ + char QRA[7]; /* 7 Qth Locator */ + char pass[13]; /* 13 Password */ + char ZIP[9]; /* 9 Zipcode */ + + struct MsgStats Total; + struct MsgStats Last; + + char CMSPass[16]; // For Secure Signon + int WebSeqNo; + + long long TimeLastConnected; //Last connection date */ + + char Filler[44 - 8]; // So we can add a few fields wirhout another resize +}; + +// flags equates + +#define F_Excluded 0x0001 +#define F_GGG 0x0002 +#define F_Expert 0x0004 +#define F_SYSOP 0x0008 +#define F_BBS 0x0010 +#define F_RMSREDIRECT 0x0020 +#define F_BBB 0x0040 +#define F_CCC 0x0080 +#define F_DDD 0x0100 +#define F_EEE 0x0200 +#define F_FFF 0x0400 +#define F_PMS 0x0800 +#define F_EMAIL 0x1000 +#define F_HOLDMAIL 0x2000 +#define F_POLLRMS 0x4000 +#define F_SYSOP_IN_LM 0x8000 +#define F_Temp_B2_BBS 0x00010000 // "Winlink Express User" +#define F_NOWINLINK 0x00020000 // Don't add Winlink.org +#define F_NOBULLS 0x00040000 +#define F_NTSMPS 0x00080000 +#define F_APRSMFOR 0x00100000 // Send APRS message for new mail +#define F_APRSSSID 0xF0000000 // (Top 4 Bits + + +struct Override +{ + char * Call; + int Days; +}; + +struct ALIAS +{ + char * Alias; + char * Dest; +}; + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGEX * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + UCHAR DIGIS[56]; // Padding in case we have digis + +}MESSAGEX, *PMESSAGEX; + + +#pragma pack() + +// Message Database Entry. Designed to be compatible with FBB + +#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy +#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists. + +#pragma pack(1) + +struct OldMsgInfo +{ + char type ; + char status ; + int number ; + int length ; + int datereceived; + char bbsfrom[7] ; // ? BBS we got it from ? + char via[41] ; + char from[7] ; + char to[7] ; + char bid[13] ; + char title[61] ; + char bin; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; + + char free[4]; + unsigned short nblu; + int theme ; + time_t datecreated ; + time_t datechanged ; + char fbbs[10] ; + char forw[10] ; + char emailfrom[41]; +} ; + + +struct MsgInfo +{ + char type; + char status; + int number; + int length; + int xdatereceived; + char bbsfrom[7]; // ? BBS we got it from ? + char via[41]; + char from[7]; + char to[7]; + char bid[13]; + char title[61]; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; + + #define B2Msg 1 // Set if Message File is a formatted B2 message + #define Attachments 2 // Set if B2 message has attachments + #define FromPaclink 4 + #define FromCMS 8 + #define FromRMSExpress 16 + #define RadioOnlyMsg 32 // Received using call-T + #define RadioOnlyFwd 64 // Received using call-R + + int xdatecreated; + int xdatechanged; + UCHAR fbbs[NBMASK]; + UCHAR forw[NBMASK]; + char emailfrom[41]; + char Locked; // Set if selected for sending (NTS Pickup) + char Defered; // FBB response '=' received + UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP) + +// For 64 bit time_t compatibility define as long long +// (so struct is same with 32 or 64 bit time_t) + + long long datereceived; + long long datecreated; + long long datechanged; + + char Spare[61 - 24]; // For future use +} ; + +#define MSGTYPE_B 0 +#define MSGTYPE_P 1 + +#define MSGSTATUS_N 0 +#define MSGSTATUS_Y 1 +#define MSGSTATUS_F 2 +#define MSGSTATUS_K 3 +#define MSGSTATUS_H 4 +#define MSGSTATUS_D 5 +#define MSGSTATUS_$ 6 + +struct NNTPRec +{ + // Used for NNTP access to Bulls + + struct NNTPRec * Next; // Record held in chain, so can be held sorted + char NewsGroup[64]; // = Bull TO.at field + int FirstMsg; // Lowest Number + int LastMsg; // Highest Number + int Count; // Active Msgs + time_t DateCreated; // COntains Creation Date of First Bull in Group +}; + + +typedef struct { + char mode; + char BID[13]; + union + { /* array named screen */ + struct + { + unsigned short msgno; + unsigned short timestamp; + }; + CIRCUIT * conn; + } u; +} BIDRec, *BIDRecP; + + +typedef struct WPDBASE{ /* 194 bytes */ + char callsign[7]; + char name[13]; + unsigned char Type; + unsigned char changed; + unsigned short seen; + long long last_modif; + long long last_seen; + char first_homebbs[41]; + char secnd_homebbs[41]; + char first_zip[9]; + char secnd_zip[9]; + char first_qth[31]; + char secnd_qth[31]; +} WPRec, * WPRecP; + +#pragma pack() + +struct FWDBAND +{ + time_t FWDStartBand; + time_t FWDEndBand; +}; + + + +struct BBSForwardingInfo +{ + // Holds info for forwarding + + BOOL Enabled; // Forwarding Enabled + char ** ConnectScript; // Main Connect Script + char ** TempConnectScript; // Used with FWD command. + int ScriptIndex; // Next line in script + BOOL MoreLines; // Set until script is finsihed + + char ** TOCalls; // Calls in to field + char ** ATCalls; // Calls in ATBBS field + char ** HaddressesP; // Heirarchical Addresses for Personals to forward to (as stored) + char *** HADDRSP; // Heirarchical Addresses for Personals to forward to + char ** Haddresses; // Heirarchical Addresses to forward to (as stored) + char *** HADDRS; // Heirarchical Addresses to forward to + int * HADDROffet; // Elements added to complete the HR. At least n+1 must match to forward + char ** FWDTimes; // Time bands to forward + struct FWDBAND ** FWDBands; + int MsgCount; // Messages for this BBS + BOOL ReverseFlag; // Set if BBS wants to poll for reverse forwarding + BOOL Forwarding; // Forward in progress + int MaxFBBBlockSize; + BOOL AllowBlocked; // Allow FBB Blocked + BOOL AllowCompressed; // Allow FBB Compressed + BOOL AllowB1; // Enable B1 + BOOL AllowB2; // Enable B2 + BOOL SendCTRLZ; // Send Ctrl/z instead of /ex + BOOL PersonalOnly; // Only Forward Personals + BOOL SendNew; // Forward new messages immediately + int FwdInterval; + int RevFwdInterval; + int FwdTimer; + time_t LastReverseForward; + char *BBSHA; // HA of BBS + char ** BBSHAElements; // elements of HA of BBS + int ConTimeout; +// char UserCall[10]; // User we are forwarding on behalf of (Currently only for RMS) +// int UserIndex; // index of User we are forwarding on behalf of (Currently only for RMS) +}; + + +struct FBBHeaderLine +{ + // Holds the info from the (up to) 5 headers presented at the start of a Forward Block + + char Format; // Ascii or Binary + char MsgType; // P B etc + char From[7]; // Sender + char ATBBS[41]; // BBS of recipient (@ Field) + char To[7]; // Recipient + char BID[13]; + int Size; + int CSize; // Compresses Size (B2 proto) + BOOL B2Message; // Set if an FC type + UCHAR * CompressedMsg; // Compressed Body fo B2 + struct MsgInfo * FwdMsg; // Header so we can mark as complete +}; + +#define MAXSTACK 20 +//#define MAXLINE 10000 +#define INPUTLEN 512 + +#define MAXLINES 1000 +#define LINELEN 200 + +char RTFHeader[4000]; + +int RTFHddrLen; + +struct ConsoleInfo +{ + struct ConsoleInfo * next; + CIRCUIT * Console; + int BPQStream; + WNDPROC wpOrigInputProc; + HWND hConsole; + HWND hwndInput; + HWND hwndOutput; + HMENU hMenu; // handle of menu + RECT ConsoleRect; + RECT OutputRect; + + int Height, Width, LastY; + + int ClientHeight, ClientWidth; + char kbbuf[INPUTLEN]; + int kbptr; + + char * readbuff; // Malloc'ed + int readbufflen; // Current Length + char * KbdStack[MAXSTACK]; + + int StackIndex; + + BOOL Bells; + BOOL FlashOnBell; // Flash instead of Beep + BOOL StripLF; + + BOOL WarnWrap; + BOOL FlashOnConnect; + BOOL WrapInput; + BOOL CloseWindowOnBye; + + unsigned int WrapLen; + int WarnLen; + int maxlinelen; + + int PartLinePtr; + int PartLineIndex; // Listbox index of (last) incomplete line + + DWORD dwCharX; // average width of characters + DWORD dwCharY; // height of characters + DWORD dwClientX; // width of client area + DWORD dwClientY; // height of client area + DWORD dwLineLen; // line length + int nCaretPosX; // horizontal position of caret + int nCaretPosY; // vertical position of caret + + COLORREF FGColour; // Text Colour + COLORREF BGColour; // Background Colour + COLORREF DefaultColour; // Default Text Colour + + int CurrentLine; // Line we are writing to in circular buffer. + + int Index; + BOOL SendHeader; + BOOL Finished; + + char OutputScreen[MAXLINES][LINELEN]; + + int Colourvalue[MAXLINES]; + int LineLen[MAXLINES]; + + int CurrentColour; + int Thumb; + int FirstTime; + BOOL Scrolled; // Set if scrolled back + int RTFHeight; // Height of RTF control in pixels + +}; + + +struct MSESSION +{ + struct MSESSION * Next; + unsigned int Key; + char * FileName; + char * OrigTimeStamp; + unsigned char * Message; + int MessageLen; + unsigned char * BlockList; + char * ID; + int BlockSize; + int BlockCount; + int BlocksReceived; + BOOL Completed; + time_t Created; + time_t LastUpdated; + int Index; // Line in Display +}; + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...); +char * strlop(char * buf, char delim); +int rt_cmd(CIRCUIT *circuit, char * Buffer); +CIRCUIT *circuit_new(CIRCUIT *circuit, int flags); +VOID BBSputs(CIRCUIT * conn, char * buf); +VOID FBBputs(CIRCUIT * conn, char * buf); +void makelinks(void); +VOID * _zalloc(size_t len); +VOID FreeChatMemory(); +VOID ChatTimer(); +VOID nputs(CIRCUIT * conn, char * buf); +VOID node_close(); +VOID removelinks(); +VOID SetupChat(); +VOID SendChatLinkStatus(); +VOID ClearChatLinkStatus(); +VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len); + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); + +// TCP Connections. FOr the moment SMTP or POP3 + +typedef struct SocketConnectionInfo +{ + struct SocketConnectionInfo * Next; + int Number; // Number of record - for Connections display + SOCKET socket; + SOCKADDR_IN sin; + int Type; // SMTP or POP3 + BOOL AMPR; // Set if sending to an AMPR.ORG server + char FromDomain[50]; // Domain we are sending from + struct UserInfo * bbs; // BBS dor forwarding to AMPR + int State; // Transaction State Machine + UCHAR CallSign[10]; + UCHAR TCPBuffer[3000]; // For converting byte stream to messages + int InputLen; // Data we have alreasdy = Offset of end of an incomplete packet; + + char * MailFrom; // Envelope Sender and Receiver + char ** RecpTo; // May be several Recipients + int Recipients; + + UCHAR * MailBuffer; // Mail Message being received. malloc'ed as needed + int MailBufferSize; // Total Malloc'ed size. Actual size is in MailSize + int MailSize; + int Flags; + + struct UserInfo * POP3User; + struct MsgInfo ** POP3Msgs; // Header List of messages for this uaer + int POP3MsgCount; // No of Messages + int POP3MsgNum; // Sequence number of message being received + + struct MsgInfo * SMTPMsg; // message for this SMTP connection + + UCHAR * SendBuffer; // Message being sent if socket is busy. malloc'ed as needed + int SendBufferSize; // Total Malloc'ed size. Actual size is in MailSize + int SendSize; // Bytes in buffer + int SendPtr; // next byte to send when ready + + struct NNTPRec * NNTPGroup; // Currently Selected Group + int NNTPNum; // Currenrly Selected Msg Number + int Timeout; // Used to close a session that is open too long + +} SocketConn; + +typedef struct KEYVALUES +{ + char * Key; + char * Value; +} KeyValues; + +typedef struct WEBMAILINFO +{ + // Info for HTML Forms Processing + + struct HtmlFormDir * Dir; // HTML Directory + char * txtFileName; // Template Name for current message + char * InputHTMLName; // Template to input message + char * DisplayHTMLName; // Template to display message + char * ReplyHTMLName; // Template for replying to message + char * txtFile; // Template data + char * OrigTo; // To field when template loaded + char * OrigSubject; // Subject field when template loaded + char * OrigBody; // Msg text when template loaded + char * OrigBID; + char OrigType; + char * To; + char * CC; + char * Subject; + char * Body; + char * BID; + char Type; + struct MsgInfo * Msg; // Msg record if replying + KeyValues txtKeys[1000]; // Key/Value pairs for txt template. Used when creating or displaying + KeyValues XMLKeys[1000]; // Key/Value pairs from XML attachment + BOOL isReply; + char * XMLName; + char * XML; // XML attachment + int XMLLen; + int Files; + char * FileName[100]; // Attachments + char * FileBody[100]; + int FileLen[100]; + + char * Header; + int HeaderLen; + + char * Footer; + int FooterLen; + + char * Reply; // put in here to save passing lots of parameters + int * RLen; + + BOOL Winlink; + BOOL P2P; + BOOL Packet; + + int CurrentMessageIndex; // Index of message currently displayed (for Prev and Next) + +}WebMailInfo; + +#define SMTPServer 1 +#define POP3SLAVE 2 +#define SMTPClient 3 +#define POP3Client 4 +#define NNTPServer 5 + +// State Values + +#define GettingUser 1 +#define GettingPass 2 +#define Authenticated 4 + +#define Connecting 8 + +// SMTP Master + +#define WaitingForGreeting 16 +#define WaitingForHELOResponse 32 +#define WaitingForFROMResponse 64 +#define WaitingForTOResponse 128 +#define WaitingForDATAResponse 256 +#define WaitingForBodyResponse 512 +#define WaitingForAUTHResponse 1024 + +// POP3 Master + +#define WaitingForUSERResponse 32 +#define WaitingForPASSResponse 64 +#define WaitingForSTATResponse 128 +#define WaitingForUIDLResponse 256 +#define WaitingForLISTResponse 512 +#define WaitingForRETRResponse 512 +#define WaitingForDELEResponse 1024 +#define WaitingForQUITResponse 2048 + + +#define SE 240 // End of subnegotiation parameters +#define NOP 241 //No operation +//#define DM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. +#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. +#define IP 244 //Suspend Interrupt or abort the process to which the NVT is connected. +#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. +#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. +#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. +#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. +#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. +#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. +#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. +#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. +#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. +#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. +#define IAC 255 + +#define suppressgoahead 3 //858 +//#define Status 5 //859 +//#define echo 1 //857 +#define timingmark 6 //860 +#define terminaltype 24 //1091 +#define windowsize 31 //1073 +#define terminalspeed 32 //1079 +#define remoteflowcontrol 33 //1372 +#define linemode 34 //1184 +#define environmentvariables 36 //1408 + +BOOL Initialise(); +#ifdef WIN32 +INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +#endif +int DisplaySessions(); +int DoStateChange(int Stream); +int DoReceivedData(int Stream); +int DoBBSMonitorData(int Stream); +int Connected(int Stream); +int Disconnected(int Stream); +//int Socket_Accept(int SocketId); +//int Socket_Data(int SocketId,int error, int eventcode); +int DataSocket_Read(SocketConn * sockptr, SOCKET sock); +int DataSocket_Write(SocketConn * sockptr, SOCKET sock); +int DataSocket_Disconnect(SocketConn * sockptr); +int RefreshMainWindow(); +int Terminate(); +int SendtoSocket(SOCKET sock,char * Msg); +int WriteLog(char * msg); +int ConnectState(int Stream); +UCHAR * EncodeCall(UCHAR * Call); +int ParseIniFile(char * fn); +struct UserInfo * AllocateUserRecord(char * Call); +struct MsgInfo * AllocateMsgRecord(); +BIDRec * AllocateBIDRecord(); +BIDRec * AllocateTempBIDRecord(); +struct UserInfo * LookupCall(char * Call); +BIDRec * LookupBID(char * BID); +BIDRec * LookupTempBID(char * BID); +VOID RemoveTempBIDS(CIRCUIT * conn); +VOID SaveUserDatabase(); +VOID GetUserDatabase(); +VOID GetMessageDatabase(); +VOID SaveMessageDatabase(); +VOID GetBIDDatabase(); +VOID SaveBIDDatabase(); +VOID GetWPDatabase(); +VOID CopyWPDatabase(); +VOID SaveWPDatabase(); +VOID GetBadWordFile(); +WPRec * LookupWP(char * Call); +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user); +VOID ProcessLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID ProcessChatLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user); +int QueueMsg( ConnectionInfo * conn, char * msg, int len); +VOID SendUnbuffered(int stream, char * msg, int len); +//int GetFileList(char * Dir); +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp); +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void DoKillCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context); +void DoReadCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call); +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call); +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB); +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); +int GetUserMsg(int m, char * Call, BOOL SYSOP); +void Flush(ConnectionInfo * conn); +VOID ClearQueue(ConnectionInfo * conn); +void TrytoSend(); +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); +struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop); +char * ReadMessageFile(int msgno); +char * ReadInfoFile(char * File); +char * FormatDateAndTime(time_t Datim, BOOL DateOnly); +int CriticalErrorHandler(char * error); +BOOL DoSendCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +BOOL CreateMessage(ConnectionInfo * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title); +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg); +int ProcessConnecting(CIRCUIT * circuit, char * Buffer, int Len); +VOID SaveConfig(char * ConfigName); +BOOL GetConfig(char * ConfigName); +int GetIntValue(config_setting_t * group, char * name); +BOOL GetStringValue(config_setting_t * group, char * name, char * value); +BOOL GetConfigFromRegistry(); +VOID Parse_SID(CIRCUIT * conn, char * SID, int len); +VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len); +VOID ProcessFBBLine(ConnectionInfo * conn, struct UserInfo * user, UCHAR * Buffer, int len); +VOID SetupNextFBBMessage(CIRCUIT * conn); +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); +int PrintMessages(HWND hDlg, int Count, int * Indexes); +int check_fwd_bit(char *mask, int bbsnumber); +void set_fwd_bit(char *mask, int bbsnumber); +void clear_fwd_bit (char *mask, int bbsnumber); +VOID SetupForwardingStruct(struct UserInfo * user); +BOOL Forward_Message(struct UserInfo * user, struct MsgInfo * Msg); +VOID StartForwarding (int BBSNumber, char ** TempScript); +BOOL Reverse_Forward(); +int ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len); +BOOL FBBDoForward(CIRCUIT * conn); +BOOL FindMessagestoForward(CIRCUIT * conn); +BOOL SeeifMessagestoForward(int BBSNumber, CIRCUIT * Conn); +int CountMessagestoForward(struct UserInfo * user); + +VOID * GetMultiLineDialogParam(HWND hDialog, int DLGItem); + +#define LIBCONFIG_STATIC +#include "libconfig.h" +VOID * GetMultiStringValue(config_setting_t * hKey, char * ValueName); +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName); + +int MultiLineDialogToREG_MULTI_SZ(HWND hWnd, int DLGItem, HKEY hKey, char * ValueName); +int Do_BBS_Sel_Changed(HWND hDlg); +VOID FreeForwardingStruct(struct UserInfo * user); +VOID FreeList(char ** Hddr); +int Do_User_Sel_Changed(HWND hDlg); +int Do_Msg_Sel_Changed(HWND hDlg); +VOID Do_Save_Msg(); +VOID Do_Add_User(HWND hDlg); +VOID Do_Delete_User(HWND hDlg); +VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user); +VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user); +VOID Do_Save_User(HWND hDlg, BOOL ShowBox); +VOID DeleteBBS(); +VOID AddBBS(); +VOID SaveBBSConfig(); +BOOL GetChatConfig(); +VOID SaveChatConfig(); +VOID SaveISPConfig(); +VOID SaveFWDConfig(); +VOID SaveMAINTConfig(); +VOID SaveWelcomeMsgs(); +VOID SavePrompts(); +VOID ReinitializeFWDStruct(struct UserInfo * user); +VOID CopyBIDDatabase(); +VOID CopyMessageDatabase(); +VOID CopyUserDatabase(); +VOID FWDTimerProc(); +VOID CreateMessageFromBuffer(CIRCUIT * conn); +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...); +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...); +VOID FreeOverrides(); +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length); +struct UserInfo * FindRMS(); +VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo); +BOOL ConnecttoBBS (struct UserInfo * user); +BOOL SetupNewBBS(struct UserInfo * user); +VOID CreateRegBackup(); +VOID SaveFilters(HWND hDlg); +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type); +BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID); +BOOL CheckifLocalRMSUser(char * FullTo); +VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Context); +BOOL wildcardcompare(char * Target, char * Match); +VOID SendWarningToSYSOP(struct MsgInfo * Msg); +VOID DoEditUserCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoPollRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoShowRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoSetIdleTime(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoFwdCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID SaveFwdParams(char * Call, struct BBSForwardingInfo * ForwardingInfo); +VOID DoAuthCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +VOID DoReroute(CIRCUIT * conn, struct UserInfo * user); + +// FBB Routines + +VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg); +VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); +VOID UnpackFBBBinary(CIRCUIT * conn); +void Decode(CIRCUIT * conn, __int16 DecodeOnly); +//long Encode(char * in, char * out, long inlen, BOOL B1Protocol); + +BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline); +VOID SaveFBBBinary(CIRCUIT * conn); +BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); +BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); + +// Console Routines + +BOOL CreateConsole(int Stream); +int WritetoConsoleWindow(int Stream, char * Msg, int len); +int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item); +void CopyRichTextToClipboard(HWND hWnd); +void CopyToClipboard(HWND hWnd); +VOID CloseConsole(int Stream); + +// Monitor Routines + +BOOL CreateMonitor(); +int WritetoMonitorWindow(char * Msg, int len); + +BOOL CreateDebugWindow(); +VOID WritetoDebugWindow(char * Msg, int len); +VOID ClearDebugWindow(); +int RemoveLF(char * Message, int len); + +// Utilities + +BOOL isdigits(char * string); +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...); + +VOID SortBBSChain(); +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG); +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup); + +// TCP Routines + +BOOL InitialiseTCP(); +VOID SetupListenSet(); +VOID TCPTimer(); +VOID TCPFastTimer(); +int Socket_Data(int sock, int error, int eventcode); +static int Socket_Accept(SOCKET SocketId); +int Socket_Connect(SOCKET sock, int Error); +VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len); +int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int Msglen, BOOL B2Flag); +BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg); +SOCKET CreateListeningSocket(int Port); +int TidyString(char * MailFrom); +VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len); +char *str_base64_encode(char *str); +int b64decode(char *str); +SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody); +BOOL POP3Connect(char * Host, int Port); +VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len); +VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len); +int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag); +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); +int SendSock(SocketConn * sockptr, char * msg); +VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...); +VOID SendFromQueue(SocketConn * sockptr); +VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes); +int CountMessagesTo(struct UserInfo * user, int * Unread); + +BOOL SendtoISP(); + +// NNTP ROutines + +VOID InitialiseNNTP(); +VOID BuildNNTPList(struct MsgInfo * Msg); +int NNTP_Data(int sock, int error, int eventcode); +int NNTP_Accept(SOCKET SocketId); + +VOID * GetOverrides(config_setting_t * group, char * ValueName); +VOID * RegGetOverrides(HKEY hKey, char * ValueName); + +VOID DoHouseKeeping(BOOL Mainual); +VOID ExpireMessages(); +VOID KillMsg(struct MsgInfo * Msg); +BOOL RemoveKilledMessages(); +VOID Renumber_Messages(); +BOOL ExpireBIDs(); +VOID MailHousekeepingResults(); +VOID CreateBBSTrafficReport(); +VOID CreateWPReport(); + +// WP Routines + +VOID ProcessWPMsg(char * MailBuffer, int Size, char * FisrtRLine); +VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime); +VOID UpdateWPWithUserInfo(struct UserInfo * user); +VOID GetWPBBSInfo(char * Rline); + +// UI Routines + +VOID SetupUIInterface(); +VOID Free_UI(); +VOID SendLatestUI(int Port); +VOID SendMsgUI(struct MsgInfo * Msg); +static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue); +VOID SeeifBBSUIFrame(struct _MESSAGEX * buff, int len); +struct MsgInfo * FindMessageByNumber(int msgno); +int CountConnectionsOnPort(int CheckPort); + +// Message Routing Routtines + +VOID SetupHAElements(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupMyHA(); +VOID SetupFwdAliases(); +struct Continent * FindContinent(char * Name); +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn); +BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo); +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); +BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); +BOOL CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); +BOOL CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); +int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo); +int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); + +VOID ReRouteMessages(); + +VOID initUTF8(); +int Is8Bit(unsigned char *cpt, int len); +int IsUTF8(unsigned char *ptr, int len); +int IsUTF8(unsigned char *ptr, int len); +int WebIsUTF8(unsigned char *ptr, int len); +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int TrytoGuessCode(unsigned char * Char, int Len); + + +VOID FreeWebMailMallocs(); + +extern int _MYTIMEZONE; + +extern HKEY REGTREE; +extern char * REGTREETEXT; + +extern HBRUSH bgBrush; +extern BOOL cfgMinToTray; + +extern CIRCUIT * Console; + +extern ULONG ChatApplMask; +extern char Verstring[]; + +extern char SignoffMsg[]; +extern char AbortedMsg[]; +extern char InfoBoxText[]; // Text to display in Config Info Popup + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +extern HWND MainWnd; +extern char BaseDir[]; +extern char BaseDirRaw[]; +extern char MailDir[]; +extern char WPDatabasePath[]; +extern char RlineVer[50]; + +extern BOOL LogBBS; +extern BOOL LogCHAT; +extern BOOL LogTCP; +extern BOOL ForwardToMe; +extern BOOL OnlyKnown; + +extern BOOL AllowAnon; +extern BOOL UserCantKillT; +extern BOOL DontNeedHomeBBS; + +extern int LatestMsg; +extern char BBSName[]; +extern char SYSOPCall[]; +extern char BBSSID[]; +extern char NewUserPrompt[]; + +extern char * WelcomeMsg; +extern char * NewWelcomeMsg; +extern char * ChatWelcomeMsg; +extern char * NewChatWelcomeMsg; +extern char * ExpertWelcomeMsg; + +extern char * Prompt; +extern char * NewPrompt; +extern char * ExpertPrompt; + +// Filter Params + +extern char ** RejFrom; // Reject on FROM Call +extern char ** RejTo; // Reject on TO Call +extern char ** RejAt; // Reject on AT Call +extern char ** RejBID; + +extern char ** HoldFrom; // Hold on FROM Call +extern char ** HoldTo; // Hold on TO Call +extern char ** HoldAt; // Hold on AT Call +extern char ** HoldBID; + +// Send WP Params + +extern BOOL SendWP; +extern char SendWPVIA[81]; +extern char SendWPTO[11]; +extern int SendWPType; + +extern int Ver[4]; + +extern struct MsgInfo ** MsgHddrPtr; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern struct NNTPRec * FirstNNTPRec; +//extern int NumberofNNTPRecs; + + +extern int NumberofMessages; +extern int FirstMessageIndextoForward; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern struct SEM AllocSemaphore; +extern struct SEM ConSemaphore; +extern struct SEM MsgNoSemaphore; + +extern struct MsgInfo * MsgnotoMsg[]; // Message Number to Message Slot List. + + +extern char hostname[]; +extern char RtUsr[]; +extern char RtUsrTemp[]; +extern char RtKnown[]; +extern int AXIPPort; +extern BOOL NeedStatus; + +extern BOOL ISP_Gateway_Enabled; +extern BOOL SMTPAuthNeeded; + + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; +extern int UserLifetime; + +extern int MaxRXSize; +extern int MaxTXSize; + +extern char OurNode[]; +extern char OurAlias[]; +extern BOOL SMTPMsgCreated; + +extern HINSTANCE hInst; +extern HWND hWnd; +extern RECT MainRect; + +extern char BBSName[]; +extern char HRoute[]; +extern char AMPRDomain[]; +extern BOOL SendAMPRDirect; +extern int BBSApplNum; +extern int SMTPInPort; +extern int POP3InPort; +extern int NNTPInPort; +extern BOOL RemoteEmail; + +extern int MaxStreams; +extern UCHAR * OtherNodes; +extern struct UserInfo * BBSChain; // Chain of users that are BBSes +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; +extern int HighestBBSNumber; +extern HMENU hFWDMenu; // Forward Menu Handle +extern char zeros[]; // For forward bitmask tests +extern BOOL EnableUI; +extern BOOL RefuseBulls; +extern BOOL SendSYStoSYSOPCall; +extern BOOL SendBBStoSYSOPCall; +extern BOOL DontHoldNewUsers; +extern BOOL DefaultNoWINLINK; +extern BOOL UIEnabled[]; +extern BOOL UINull[]; +extern BOOL UIMF[]; +extern BOOL UIHDDR[]; +extern char * UIDigi[]; +extern int MailForInterval; +extern char MailForText[]; + +extern BOOL ISP_Gateway_Enabled; + +extern char MyDomain[]; // Mail domain for BBS<>Internet Mapping + +extern char ISPSMTPName[]; +extern char ISPEHLOName[]; +extern int ISPSMTPPort; + +extern char ISPPOP3Name[]; +extern int ISPPOP3Port; +extern int ISPPOP3Interval; + +extern char ISPAccountName[]; +extern char ISPAccountPass[]; +extern char EncryptedISPAccountPass[]; +extern int EncryptedPassLen; +extern char *month[]; + +extern HWND hDebug; +extern RECT MonitorRect; +extern RECT DebugRect; +extern HWND hMonitor; +//extern HWND hConsole; +//extern RECT ConsoleRect; +extern int LogAge; +extern BOOL DeletetoRecycleBin; +extern BOOL SuppressMaintEmail; +extern BOOL SaveRegDuringMaint; +extern BOOL SendWP; +extern BOOL OverrideUnsent; +extern BOOL SendNonDeliveryMsgs; +extern BOOL GenerateTrafficReport; + +extern double PR; +extern double PUR; +extern double PF; +extern double PNF; +extern int BF; +extern int BNF; +extern int NTSD; +extern int NTSU; +extern int NTSF; +extern int AP; +extern int AB; + +extern struct Override ** LTFROM; +extern struct Override ** LTTO; +extern struct Override ** LTAT; + +extern time_t LastHouseKeepingTime; +extern time_t LastTrafficTime; + +extern char * MyElements[]; +extern char ** AliasText; +extern struct ALIAS ** Aliases; + +extern BOOL ReaddressLocal; +extern BOOL ReaddressReceived; +extern BOOL WarnNoRoute; +extern BOOL SendPtoMultiple; +extern BOOL Localtime; + +struct ConsoleInfo * ConsHeader[2]; + +extern BOOL NeedHomeBBS; +extern char ConfigName[250]; +extern BOOL UsingingRegConfig; + +extern BOOL MulticastRX; + +extern BOOL FilterWPBulls; +extern BOOL NoWPGuesses; +extern char ** SendWPAddrs; // Replacers WP To and VIA + +extern BOOL DontCheckFromCall; + +// YAPP stuff + +#define SOH 1 +#define STX 2 +#define ETX 3 +#define EOT 4 +#define ENQ 5 +#define ACK 6 +#define DLE 0x10 +#define NAK 0x15 +#define CAN 0x18 diff --git a/cMain.c b/cMain.c index e6c1d88..9acb24a 100644 --- a/cMain.c +++ b/cMain.c @@ -2220,8 +2220,17 @@ L2Packet: memcpy(BBuffer, Message, Message->LENGTH); BBuffer->PORT = toPort; BPORT = GetPortTableEntryFromPortNum(toPort); + if (BPORT) + { + if (BPORT->SmartIDInterval && BPORT->SmartIDNeeded == 0) + { + // Using Smart ID, but none scheduled + + BPORT->SmartIDNeeded = time(NULL) + BPORT->SmartIDInterval; + } PUT_ON_PORT_Q(BPORT, BBuffer); + } else ReleaseBuffer(BBuffer); } diff --git a/debug/BuildLog.htm b/debug/BuildLog.htm new file mode 100644 index 0000000..9898b88 Binary files /dev/null and b/debug/BuildLog.htm differ diff --git a/debug/MCP2221.exe.embed.manifest b/debug/MCP2221.exe.embed.manifest new file mode 100644 index 0000000..3d9330a --- /dev/null +++ b/debug/MCP2221.exe.embed.manifest @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/debug/MCP2221.exe.embed.manifest.res b/debug/MCP2221.exe.embed.manifest.res new file mode 100644 index 0000000..7b55d19 Binary files /dev/null and b/debug/MCP2221.exe.embed.manifest.res differ diff --git a/debug/MCP2221.exe.intermediate.manifest b/debug/MCP2221.exe.intermediate.manifest new file mode 100644 index 0000000..1b520e1 --- /dev/null +++ b/debug/MCP2221.exe.intermediate.manifest @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/debug/MCP2221.obj b/debug/MCP2221.obj new file mode 100644 index 0000000..5890fa1 Binary files /dev/null and b/debug/MCP2221.obj differ diff --git a/debug/bpq32.pdb b/debug/bpq32.pdb new file mode 100644 index 0000000..93e46ad Binary files /dev/null and b/debug/bpq32.pdb differ diff --git a/debug/hid.obj b/debug/hid.obj new file mode 100644 index 0000000..8fc95b4 Binary files /dev/null and b/debug/hid.obj differ diff --git a/debug/mt.dep b/debug/mt.dep new file mode 100644 index 0000000..b118ee7 --- /dev/null +++ b/debug/mt.dep @@ -0,0 +1 @@ +Manifest resource last updated at 15:17:36.17 on 21/07/2023 diff --git a/debug/vc80.idb b/debug/vc80.idb new file mode 100644 index 0000000..390465e Binary files /dev/null and b/debug/vc80.idb differ diff --git a/debug/vc80.pdb b/debug/vc80.pdb new file mode 100644 index 0000000..6006792 Binary files /dev/null and b/debug/vc80.pdb differ diff --git a/kiss.c b/kiss.c index 1f52345..0be9043 100644 --- a/kiss.c +++ b/kiss.c @@ -492,7 +492,7 @@ HANDLE OpenConnection(struct PORTCONTROL * PortVector) ASYSEND(PortVector, ENCBUFF, 5); } - if (KISS->KISSCMD && KISS->KISSCMDLEN) + if (KISS && KISS->KISSCMD && KISS->KISSCMDLEN) ASYSEND(PortVector, KISS->KISSCMD, KISS->KISSCMDLEN); @@ -1890,9 +1890,7 @@ VOID ConnecttoTCPThread(NPASYINFO ASY) ioctlsocket (sock, FIONBIO, ¶m); - // If configured send TNC command - - if (KISS->KISSCMD && KISS->KISSCMDLEN) + if (KISS && KISS->KISSCMD && KISS->KISSCMDLEN) send(sock, KISS->KISSCMD, KISS->KISSCMDLEN, 0); continue; diff --git a/tncinfo.h b/tncinfo.h index 299d7c9..8e0272c 100644 --- a/tncinfo.h +++ b/tncinfo.h @@ -626,9 +626,10 @@ typedef struct TNCINFO HANDLE hDevice; - int ReopenTimer; // Used to reopen device if failed (eg USB port removed) + int ReopenTimer; // Used to reopen device if failed (eg USB port removed) BOOL HostMode; // Set if in DED Host Mode // BOOL CRCMode; // Set if using SCS Extended DED Mode (JHOST4) + BOOL UsingTermMode; // Set if tnc should be left in term mode int Timeout; // Timeout response counter int Retries; int Window; // Window Size for ARQ @@ -676,6 +677,7 @@ typedef struct TNCINFO BOOL Robust; // Set if SCS Tracker is in Robust Packet mode or WINMOR TNC is in Robust Mode BOOL RobustDefault; // Set if SCS Tracker default is Robust Packet mode BOOL ForceRobust; // Don't allow Normal Packet even if scan requests it. + BOOL TeensyRPR; // Teensy RPR TNC - don't send %R char NormSpeed[8]; // Speed Param for Normal Packet on Tracker char RobustSpeed[8]; // Speed Param for Robust Packet on Tracker BOOL RPBEACON; // Send Beacon after each session