/* 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); 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 = 33; 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) 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])) 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])) 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; // 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) // = 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); #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")) { char * Cmd; 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, "RADIOONLY", 9) == 0) { conn->BBSFlags |= WINLINKRO; 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")) // URONODE { conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered 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; 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; } // 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; } 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); } #ifdef FREEBSD 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 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"); nodeprintf(conn, BBSSID, "BPQ-", Ver[0], Ver[1], Ver[2], Ver[3], BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", BLOCKED ? "FW": "", WL2KRO ? "" : "J"); // if (user->flags & F_Temp_B2_BBS) // nodeprintf(conn,";PQ: 66427529\r"); // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); } if ((user->Name[0] == 0) & AllowAnon) strcpy(user->Name, user->Call); if (user->Name[0] == 0) { conn->Flags |= GETTINGUSER; BBSputs(conn, NewUserPrompt); } else SendWelcomeMsg(Stream, conn, user); 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); 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; 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, 4); 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; // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ conn->Flags |= SENDTITLE; Msg = conn->FwdMsg; 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)); BBSputs(conn, "OK\r"); return; } if (_memicmp(Buffer, "POSYNCHELLO", 11) == 0) { 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; } void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) { Buffer[len] = 0; if (strcmp(Buffer, "OK\r") == 0) { WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); // Propose any waiting files BBSputs(conn, "BYE\r"); conn->CloseAfterFlush = 20; // 2 Secs conn->BBSFlags &= ~SYNCMODE; return; } if (memcmp(Buffer, "TR AddMessage_", 14) == 0) { BIDRec * BID; char *ptr1, *ptr2, *context; // TR AddMessage_1145_G8BPQ 727 1202 440 True WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); ptr1 = strtok_s(&Buffer[14], " ", &context); // MID ptr2 = strtok_s(NULL, " ", &context); conn->SyncCompressedLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->SyncXMLLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->SyncMsgLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); BID = LookupBID(ptr1); if (BID) { BBSputs(conn, "Rejected - Duplicate BID\r"); return; } conn->TempMsg = zalloc(sizeof(struct MsgInfo)); BBSputs(conn, "OK\r"); return; } if (memcmp(Buffer, "TR RequestSync_", 15) == 0) { char *ptr1, *ptr2, *context; // TR RequestSync_G8BPQ_14 224 417 0 True WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); ptr1 = strtok_s(&Buffer[15], " ", &context); // MID ptr2 = strtok_s(NULL, " ", &context); conn->SyncCompressedLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->SyncXMLLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->SyncMsgLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->TempMsg = zalloc(sizeof(struct MsgInfo)); BBSputs(conn, "OK\r"); return; } if (memcmp(Buffer, "TR Delivered_", 13) == 0) { char *ptr1, *ptr2, *context; // TR RequestSync_G8BPQ_14 224 417 0 True WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); ptr1 = strtok_s(&Buffer[13], " ", &context); // MID ptr2 = strtok_s(NULL, " ", &context); conn->SyncCompressedLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->SyncXMLLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->SyncMsgLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->TempMsg = zalloc(sizeof(struct MsgInfo)); BBSputs(conn, "OK\r"); return; } if (memcmp(Buffer, "TR Remove_", 10) == 0) { char *ptr1, *ptr2, *context; // TR RequestSync_G8BPQ_14 224 417 0 True WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); ptr1 = strtok_s(&Buffer[10], " ", &context); // MID ptr2 = strtok_s(NULL, " ", &context); conn->SyncCompressedLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->SyncXMLLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->SyncMsgLen = atoi(ptr2); ptr2 = strtok_s(NULL, " ", &context); conn->TempMsg = zalloc(sizeof(struct MsgInfo)); BBSputs(conn, "OK\r"); return; } if (strcmp(Buffer, "BYE\r") == 0) { WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); 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; } // 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); BBSputs(conn, "OK\r"); return; } 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); } int ReformatSyncMessage(CIRCUIT * conn) { // Message has been decompressed - reformat to look lime 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]; int partLen[100]; // Message has an XML header which I don't think we need, then the message /* 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); if (conn->SyncMsgLen == 0) 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 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 Partlen = ptr - Msgptr; ptr += (BLen + 2); // End of Boundary if (*ptr == '-') // Terminating Boundary Input = NULL; else Input = ptr + 2; // Part Starts with header (content-type, etc), but skip for now part[i] = strstr(Msgptr, "\r\n\r\n"); // Over separator if (part[i]) { part[i] += 4; partLen[i] = Partlen - (part[i] - Msgptr) - 2; } Msgptr = ptr = Input; i++; } } else 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]) { Msg->B2Flags |= Attachments; NewMsg += sprintf(NewMsg, "File: %d %s\r\n", partn[i], partName[i]); } */ NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header // Now add parts memmove(NewMsg, part[0], partLen[0]); NewMsg += partLen[0]; 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; }