diff --git a/AGWAPI.c b/AGWAPI.c index dd85c48..c28d8f3 100644 --- a/AGWAPI.c +++ b/AGWAPI.c @@ -276,9 +276,9 @@ VOID Poll_AGW() } -SOCKADDR_IN local_sin; /* Local socket - internet style */ +static SOCKADDR_IN local_sin; /* Local socket - internet style */ -PSOCKADDR_IN psin; +static PSOCKADDR_IN psin; BOOL AGWAPIInit() @@ -343,19 +343,19 @@ BOOL AGWAPIInit() { if (PORT->Hide == 0) { - VisiblePortToRealPort[v++] = i - 1; - memcpy(ptr,"Port",4); - ptr += sprintf(ptr, "%d", i); - memcpy(ptr, " with ", 6); - ptr+=6; - memcpy(ptr, PORT->PORTDESCRIPTION, 29); // ";" - ptr+=29; - - while (*(--ptr) == ' ') {} + VisiblePortToRealPort[v++] = i - 1; + memcpy(ptr,"Port",4); + ptr += sprintf(ptr, "%d", i); + memcpy(ptr, " with ", 6); + ptr+=6; + memcpy(ptr, PORT->PORTDESCRIPTION, 29); // ";" + ptr+=29; - ptr++; + while (*(--ptr) == ' ') {} - *(ptr++)=';'; + ptr++; + + *(ptr++)=';'; } i++; PORT=PORT->PORTPOINTER; @@ -364,16 +364,15 @@ BOOL AGWAPIInit() *(ptr)=0; AGWMONVECPTR->HOSTAPPLFLAGS = 0x80; // Requext Monitoring - + return TRUE; } - int SetUpHostSessions() { int Stream, i; if (AGWMask == 0) return 0; - + for (i = 1; i <= AGWSessions; i++) { strcpy(pgm, "AGW"); diff --git a/APRSCode.c b/APRSCode.c index 1749795..67ab0c2 100644 --- a/APRSCode.c +++ b/APRSCode.c @@ -117,7 +117,7 @@ BOOL LogAPRSIS = FALSE; static char ConfigClassName[]="CONFIG"; -BPQVECSTRUC * APRSMONVECPTR; +extern BPQVECSTRUC * APRSMONVECPTR; extern int MONDECODE(); extern VOID * zalloc(int len); diff --git a/ARDOP.c b/ARDOP.c index d24f3db..01ee591 100644 --- a/ARDOP.c +++ b/ARDOP.c @@ -861,6 +861,14 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + // Check session limit timer if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) @@ -1867,7 +1875,7 @@ VOID ARDOPReleaseTNC(struct TNCINFO * TNC) } -VOID ARDOPSuspendPort(struct TNCINFO * TNC) +VOID ARDOPSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); } diff --git a/BBSUtilities.c b/BBSUtilities.c index 87d21f0..487b6a9 100644 --- a/BBSUtilities.c +++ b/BBSUtilities.c @@ -222,10 +222,25 @@ extern BOOL EventsEnabled; extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; +/* --- TAJ PG Server--- */ + +void run_pg( CIRCUIT * conn, struct UserInfo * user ); +void startrun_pgThread( RUNPGARGS_PTR Args ); + +char * SERVERLIST[256][3]; + +int NUM_SERVERS = 0; + +/*------- TAJ END ----------*/ + + #ifdef LINBPQ extern BPQVECSTRUC ** BPQHOSTVECPTR; extern char WL2KModes [54][18]; + + #else + __declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; @@ -1020,6 +1035,20 @@ Next: } SortBBSChain(); + +/*------- TAJ PG SERVER ----------*/ + +#ifndef WIN32 + printf("Number of PG Servers = %d\n", NUM_SERVERS ); + + for ( int i=0; i< NUM_SERVERS; i++ ) { + printf("Server #%d\t%s\tExec->%s\tDESC->%s\n", i, SERVERLIST[i][0], SERVERLIST[i][1], SERVERLIST[i][2]); + } + + +#endif +/*------- TAJ END ----------*/ + } VOID CopyUserDatabase() @@ -9946,7 +9975,7 @@ BOOL GetConfig(char * ConfigName) return(EXIT_FAILURE); } -#if IBCONFIG_VER_MINOR > 4 +#if LIBCONFIG_VER_MINOR > 5 config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); #else config_set_auto_convert (&cfg, 1); @@ -10624,6 +10653,15 @@ int Disconnected (int Stream) conn->InputBufferLen = 0; } + /* ---- TAJ PG SERVER ---- */ + if ( conn->UserPointer->Temp->RUNPGPARAMS ) { + printf("Freeing RUNPGPARAMS\n"); + free(conn->UserPointer->Temp->RUNPGPARAMS); + conn->UserPointer->Temp->RUNPGPARAMS = NULL; + } + + /*------- TAJ END --------- */ + if (conn->InputMode == 'B') { // Save partly received message for a restart @@ -10738,6 +10776,17 @@ int DoReceivedData(int Stream) return 0; } + + /* ---------- TAJ START - PG server --------- */ + + if (conn->InputMode == 'P') // Inside PG Server + { + user = conn->UserPointer; + run_pg(conn, user); + return 0; + } + /* ---------- TAJ END --------- */ + if (conn->InputMode == 'B') { // if in OpenBCM mode, remove FF transparency @@ -11487,6 +11536,527 @@ extern UCHAR * infile; BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen); +/* ---TAJ PG Server --- */ +#ifndef WIN32 + +#define verbose 1 +#define TRUE 1 +#define FALSE 0 +#include +#include + +typedef struct _POPENRET +{ + FILE * fp; + pid_t pid; +} POPENRET; + +/* +* Check if the PG is still running after 5 sec +* if so, kill it +*/ +void run_pgTimeoutThread( pid_t process ) +{ + + printf("watchdog thread: PID of subprocess: %d\n", process); + fflush(stdout); + Sleep(5000); + // if still running PID (?) then kill. + if ( getpgid(process) >= 0 ) { + printf("watchdog thread: Still running, so killing %d ... ", process); +// if ( kill( process, SIGTERM ) == 0 ) { + if ( kill( -process, SIGKILL ) == 0 ) { + printf("Killed\n"); + } else { + printf("Failed\n"); + } + } + printf("watchdog thread: PID=%d Exit\n", process); + fflush(stdout); + //return; +} + + +//https://sources.debian.org/src/cron/3.0pl1-45/popen.c/ + +POPENRET my_popen(char *program, char *type, CIRCUIT *conn) +{ + register char *cp; + FILE *iop; + int argc, pdes[2]; + pid_t pid; + POPENRET PRET; + + if (*type != 'r' && *type != 'w' || type[1]) + return(PRET); + if (pipe(pdes) < 0) + return(PRET); + + iop = NULL; + switch(pid = fork()) { + case -1: /* error */ + (void)close(pdes[0]); + (void)close(pdes[1]); + return PRET; + case 0: /* child */ + if (*type == 'r') { + if (pdes[1] != 1) { + dup2(pdes[1], 1); + dup2(pdes[1], 2); /* stderr, too! */ + (void)close(pdes[1]); + } + (void)close(pdes[0]); + } else { + if (pdes[0] != 0) { + dup2(pdes[0], 0); + (void)close(pdes[0]); + } + (void)close(pdes[1]); + } + setpgid(-pid,pid); + char *args[] = {"sh", "-c", program, NULL}; + execve("/bin/sh", args, NULL); + _exit(1); + } + + /* parent; assume fdopen can't fail... */ + + printf("PID=%d\n", pid ); + _beginthread((void (*)(void *))run_pgTimeoutThread, 0, (VOID *) pid ); + + if (*type == 'r') { + iop = fdopen(pdes[0], type); + (void)close(pdes[1]); + } else { + iop = fdopen(pdes[1], type); + (void)close(pdes[0]); + } + + char buffer[128]; + while (fgets(buffer, sizeof(buffer), iop) != NULL) { + BBSputs(conn, buffer); +// printf("%s", buffer); +// sleep(200); + buffer[0] = '\0'; + } +// BBSputs(conn,"\n"); + PRET.fp = iop; + PRET.pid= pid; +// (void)close(pdes[0]); +// (void)close(pdes[1]); + + return(PRET); +} + +int +my_pclose( POPENRET pret ) +{ + register int fdes; + sigset_t omask, mask; + int stat_loc; + pid_t pid; + FILE * iop = pret.fp; + + fdes = fileno(iop); + (void)fclose(iop); + + sigemptyset(&mask); + sigaddset(&mask, SIGQUIT); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGHUP); + sigprocmask(SIG_BLOCK, &mask, &omask); + pid = waitpid(pret.pid, &stat_loc, 0); + sigprocmask(SIG_SETMASK, &omask, NULL); + if (pid == -1 || !WIFEXITED(stat_loc)) + return -1; +// return WEXITSTATUS(stat_loc); + printf( "return = %d\n", WEXITSTATUS(stat_loc)); + return stat_loc; +} + + +int run_server (char **cmd, int nb_cmd, int mode, char *log, char *pgdir, char *data, CIRCUIT * conn) +{ + int i; + int ret = 0; + FILE *fp; + POPENRET PRET; + pid_t pid; + char *ptr; + char file[256]; + char buf[256]; + char dir[256]; + char arg[256]; + + if (mode) +// sprintf (file, " %s", log); +// sprintf (file, " >>%s", log); +// sprintf (file, " | tee -a %s", log); + sprintf(file, "" ); + else + sprintf (file, " > 8; + + if (verbose) { + printf ("Debug: command = {%s}\n", buf); + printf ("Debug: exit code = %d\n", ret); + } + + /* fail-safe bypass if filter executable isn't found (exit code 127) (was ret ==127)*/ + if (ret > 5) // should never be more than 5 + ret = 0; + } + return ( ret ); +} + + +void run_pg( CIRCUIT * conn, struct UserInfo * user ) +{ + + if (!user->Temp->RUNPGPARAMS) { +// printf("Allocating new RUNPGPARAMS\n"); + user->Temp->RUNPGPARAMS = (RUNPGARGS_PTR) zalloc(sizeof(RUNPGARGS)); + } + + user->Temp->RUNPGPARAMS->user = user; + user->Temp->RUNPGPARAMS->conn = conn; + strncpy(user->Temp->RUNPGPARAMS->InputBuffer, conn->InputBuffer, 80); // needs to be length of actual input! + user->Temp->RUNPGPARAMS->Len = conn->InputLen; + + if ( conn == 0 || user == 0 ) { + printf("run_pg null err\n"); + return; + } + + _beginthread((void (*)(void *))startrun_pgThread, 0, user->Temp->RUNPGPARAMS ); + + return; +} + + +void startrun_pgThread( RUNPGARGS_PTR Args ) { + + CIRCUIT * conn = Args->conn; + struct UserInfo * user = Args->user; + + char cmd[20]; + sprintf( cmd, "./%s", SERVERLIST[user->Temp->PG_SERVER][1] ); + char *ptr = cmd; + char pg_dir[] = "/home/pi/linbpq/linbpq/downloads/new/linbpq/pg/"; + char log_file[50] = "pg.log"; + char call[6]; + char data[80]; + char line[80]; + char *line_ptr = line; + int index; + char *data_ptr = data; + size_t bufsize = 80; + + strcpy( call, conn->UserPointer->Call); +// sprintf( log_file, "%s-%d.log", call, conn); + index = user->Temp->PG_INDEX; + + line[0] = '\0'; + int Len = Args->Len; + UCHAR * Msg = Args->InputBuffer; + strncpy( line, Msg, Len); + line[ Len - 1 ] = 0; //remove LF + + sprintf( data, "%s %d 0 0 %s", call, index, line); + + // clear the input queue + conn->InputLen = 0; + conn->InputBufferLen = 0; + + int ret = run_server (&ptr, 1, 1, log_file, pg_dir, data_ptr, conn); + + switch (ret) + { + case -1: // ERROR or forced closed + case 0: index=0; // Goodbye/Exit + conn->InputMode=0; + SendPrompt(conn, user); + break; + case 1: index++; // inc & keep in PG + break; + case 2: index=0; // disconnect + conn->InputMode=0; + Disconnect(conn->BPQStream); + break; + case 3: printf("data->BBS & end\n"); + break; + case 4: printf("data->BBS and inc %d\n", index++); + break; + case 5: printf("call no inc %d\n", ret); + break; + + } + + user->Temp->PG_INDEX=index; +} +/*---- TAJ END ----- */ + +#else + +#define BUFSIZE 4096 + +HANDLE g_hChildStd_IN_Rd = NULL; +HANDLE g_hChildStd_IN_Wr = NULL; +HANDLE g_hChildStd_OUT_Rd = NULL; +HANDLE g_hChildStd_OUT_Wr = NULL; + +HANDLE g_hInputFile = NULL; + +int CreateChildProcess(void); +void WriteToPipe(void); +void ReadFromPipe(void); + + +void run_pg( CIRCUIT * conn, struct UserInfo * user ) +{ + // Run PG program, rend anything from program's stdout to the user + + int retcode = -1; + SECURITY_ATTRIBUTES saAttr; + char szCmdline[256] = "C:\\test\\hello.exe g8bpq 0"; + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + BOOL bSuccess = FALSE; + DWORD dwRead, dwWritten; + CHAR chBuf[BUFSIZE]; + int index = 0; + int ret = 0; + + // if first entay allocate RUNPGPARAMS + if (!user->Temp->RUNPGPARAMS) + { + user->Temp->RUNPGPARAMS = (RUNPGARGS_PTR) zalloc(sizeof(RUNPGARGS)); + } + + user->Temp->RUNPGPARAMS->user = user; + user->Temp->RUNPGPARAMS->conn = conn; + strncpy(user->Temp->RUNPGPARAMS->InputBuffer, conn->InputBuffer, 80); // needs to be length of actual input! + user->Temp->RUNPGPARAMS->Len = conn->InputLen; + index = user->Temp->PG_INDEX; + + conn->InputBuffer[conn->InputLen] = 0; + strlop(conn->InputBuffer, 13); + conn->InputLen = 0; + + // Build command line. Parmas are: + + // - Callsign (format as F6FBB-8). + // - Level number (0 is the first time, up to 99). + // - Flags of the user (binary number as user`s mask of INIT.SRV). + // - Record number of the user in INF.SYS. + // - Received data (each word is a new argument). + + // BPQ doesn't support params 3 and 4 (but may supply copy of user record later) + + sprintf(szCmdline, "%s/PG/%s %s %d 0 0 %s", BaseDir, + SERVERLIST[user->Temp->PG_SERVER][1], user->Call, index, conn->InputBuffer); + + // Set the bInheritHandle flag so pipe handles are inherited. + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // Create a pipe for the child process's STDOUT. + + if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) + return; + + // Ensure the read handle to the pipe for STDOUT is not inherited. + + if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ) + return; + + // Create the child process. + + + // Set up members of the PROCESS_INFORMATION structure. + + ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); + + // Set up members of the STARTUPINFO structure. + // This structure specifies the STDIN and STDOUT handles for redirection. + + ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = g_hChildStd_OUT_Wr; + siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; + siStartInfo.hStdInput = g_hChildStd_IN_Rd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + // Create the child process. + + bSuccess = CreateProcess(NULL, + szCmdline, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo); // receives PROCESS_INFORMATION + + // If an error occurs, exit the application. + + if (!bSuccess) + retcode = -1; + else + { + // Wait until child process exits. + + if (WaitForSingleObject(piProcInfo.hProcess, 5000) == 0) // Wait max 5 seconds + { + // Success + + GetExitCodeProcess(piProcInfo.hProcess, &retcode); + } + else + { + // Failed or ran too long - kill + + TerminateProcess(piProcInfo.hProcess, 0); + } + + // Close handles to the child process and its primary thread. + // Some applications might keep these handles to monitor the status + // of the child process, for example. + + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + + // Close handles to the stdin and stdout pipes no longer needed by the child process. + // If they are not explicitly closed, there is no way to recognize that the child process has ended. + + CloseHandle(g_hChildStd_OUT_Wr); + + // Send output to User + + for (;;) + { + bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); + if( ! bSuccess || dwRead == 0 ) break; + + chBuf[dwRead] = 0; + + if (retcode == 4) + ProcessLine(conn, user, chBuf, dwRead); + else + BBSputs(conn, chBuf); + + if (! bSuccess ) break; + } + } + + + switch (retcode) + { + case -1: // ERROR or forced closed + + BBSputs(conn, "Problem running PG program\r"); + index=0; + conn->InputMode=0; + SendPrompt(conn, user); + break; + + case 0: + + // Goodbye/Exit + + index=0; + conn->InputMode=0; + SendPrompt(conn, user); + break; + + case 1: + index++; // inc & keep in PG + break; + + case 2: + index=0; // disconnect + conn->InputMode=0; + Disconnect(conn->BPQStream); + break; + + case 3: + printf("data->BBS & end\n"); + break; + + case 4: + + // Send Output to BBS - was down above + break; + + case 5: + printf("call no inc %d\n", ret); + break; + } + + user->Temp->PG_INDEX=index; + + // The remaining open handles are cleaned up when this process terminates. + // To avoid resource leaks in a larger application, close handles explicitly. + + return; +} + + + +#endif + VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) { @@ -12293,6 +12863,55 @@ VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) return; } + /*---- TAJ PG Server ----- */ + + + if (_stricmp(Cmd, "PG") == 0) + { + if ( NUM_SERVERS == 0 ) + { + BBSputs(conn, "No PG Servers currently defined\r"); + SendPrompt(conn, user); + Flush(conn); + return; + } + + if ( !Arg1 ) + { + char reply[80]; + int i; + for (i=0; i< NUM_SERVERS; i++ ) + { + sprintf(reply, "%s -> %s\r", SERVERLIST[i][0], SERVERLIST[i][2]); + BBSputs(conn, reply); + } + SendPrompt(conn, user); + return; + } + else + { + int i; + for (i=0; i < NUM_SERVERS; i++ ) + { + if ( _stricmp( _strupr(Arg1), SERVERLIST[i][0] ) == 0 ) { + user->Temp->PG_SERVER = i; // index to server to run + user->Temp->PG_INDEX = 0; // newly starting PG + conn->InputMode = 'P'; // Inside PG Server + + // conn->InputBuffer is altered above and split into Cmd,Arg1,Context + // so put it back and call PG (removing PG) + sprintf( conn->InputBuffer, "%s", Context); + conn->InputLen = strlen(Context); + run_pg( conn, user ); + return; + } + } + } + + } + + /*---- TAJ END ---- */ + if (conn->Flags == 0) { if (!CheckForTooManyErrors(conn)) @@ -14924,3 +15543,47 @@ int decode_quoted_printable(char *ptr, int len) } return ptr2 - Start; } + + +VOID GetPGConfig() +{ + char FN[256]; + FILE *file; + char buf[256],errbuf[256]; + char * p_prog, * p_name, * p_desc; + int n = 0; + + + strcpy(FN, BaseDir); + strcat(FN, "/"); + strcat(FN, "PG/PGList.txt"); + + if ((file = fopen(FN, "r")) == NULL) + return; + + while(fgets(buf, 255, file) != NULL) + { + strcpy(errbuf,buf); // save in case of error + + p_prog = strtok(buf, ",\n\r"); + p_name = strtok(NULL, ",\n\r"); + p_desc = strtok(NULL, ",\n\r"); + + + if (p_desc && p_desc[0]) + { + while(*(p_name) == ' ') // Remove leading spaces + p_name++; + while(*(p_desc) == ' ') + p_desc++; + SERVERLIST[n][0] = _strdup(p_prog); + SERVERLIST[n][1] = _strdup(p_name); + SERVERLIST[n++][2] = _strdup(p_desc); + } + if (n > 255) + break; + } + + NUM_SERVERS = n; + fclose(file); +} diff --git a/BBSUtilities.c.bak b/BBSUtilities.c.bak new file mode 100644 index 0000000..b60c77d --- /dev/null +++ b/BBSUtilities.c.bak @@ -0,0 +1,14926 @@ +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Utility Routines + +#include "bpqmail.h" +#ifdef WIN32 +#include "Winspool.h" +#else +#include +#endif + + +BOOL Bells; +BOOL FlashOnBell; // Flash instead of Beep +BOOL StripLF; + +BOOL WarnWrap; +BOOL FlashOnConnect; +BOOL WrapInput; +BOOL CloseWindowOnBye; + +RECT ConsoleRect; + +BOOL OpenConsole; +BOOL OpenMon; + +int reportNewMesageEvents = 0; + + +extern struct ConsoleInfo BBSConsole; + +extern char LOC[7]; + +//#define BBSIDLETIME 120 +//#define USERIDLETIME 300 + + +#define BBSIDLETIME 900 +#define USERIDLETIME 900 + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +UCHAR * GetLogDirectory(); +DllExport int APIENTRY SessionStateNoAck(int stream, int * state); +int RefreshWebMailIndex(); +#else +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; +typedef char * (WINAPI FAR *FARPROCZ)(); +typedef int (WINAPI FAR *FARPROCX)(); +FARPROCZ pGetLOC; +FARPROCX pRefreshWebMailIndex; + +#endif + +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); +VOID APIENTRY md5 (char *arg, unsigned char * checksum); +int APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +void DeletetoRecycle(char * FN); +VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID TidyPrompts(); +char * ReadMessageFileEx(struct MsgInfo * MsgRec); +char * APIENTRY GetBPQDirectory(); +BOOL SendARQMail(CIRCUIT * conn); +int APIENTRY ChangeSessionIdletime(int Stream, int idletime); +int APIENTRY GetApplNum(int Stream); +VOID FormatTime(char * Time, time_t cTime); +BOOL CheckifPacket(char * Via); +char * APIENTRY GetVersionString(); +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +int GetCMSHash(char * Challenge, char * Password); +BOOL SendAMPRSMTP(CIRCUIT * conn); +VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen); +VOID MCastTimer(); +VOID MCastConTimer(ConnectionInfo * conn); +int FindFreeBBSNumber(); +VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL ProcessYAPPMessage(CIRCUIT * conn); +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void YAPPSendData(ConnectionInfo * conn); +VOID CheckBBSNumber(int i); +struct UserInfo * FindAMPR(); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); +char *stristr (char *ch1, char *ch2); +BOOL CheckforMessagetoServer(struct MsgInfo * Msg); +void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled); +void ListCategories(ConnectionInfo * conn); +void RebuildNNTPList(); +long long GetInt64Value(config_setting_t * group, char * name); +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +int ReformatSyncMessage(CIRCUIT * conn); +char * initMultipartUnpack(char ** Input); +char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg); +int decode_quoted_printable(char *ptr, int len); +void decodeblock( unsigned char in[4], unsigned char out[3]); +int encode_quoted_printable(char *s, char * out, int Len); +int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); +int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); + +config_t cfg; +config_setting_t * group; + +extern ULONG BBSApplMask; + +//static int SEMCLASHES = 0; + +char SecureMsg[80] = ""; // CMS Secure Signon Response + +int NumberofStreams; + +extern char VersionStringWithBuild[50]; + +#define MaxSockets 64 + +extern struct SEM OutputSEM; + +extern ConnectionInfo Connections[MaxSockets+1]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + + +extern char MailDir[MAX_PATH]; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern BIDRec ** TempBIDRecPtr; +extern int NumberofTempBIDs; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern char ** BadWords; +extern int NumberofBadWords; +extern char * BadFile; + +extern int LatestMsg; +extern struct SEM MsgNoSemaphore; // For locking updates to LatestMsg +extern int HighestBBSNumber; + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; + +extern int ProgramErrors; + +extern BOOL MonBBS; +extern BOOL MonCHAT; +extern BOOL MonTCP; + +BOOL SendNewUserMessage = TRUE; +BOOL AllowAnon = FALSE; +BOOL UserCantKillT = FALSE; + +typedef int (WINAPI FAR *FARPROCX)(); +FARPROCX pRunEventProgram; + +int RunEventProgram(char * Program, char * Param); + + +extern BOOL EventsEnabled; + +#define BPQHOSTSTREAMS 64 + +// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 + +extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +extern char WL2KModes [54][18]; +#else +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; + + +char WL2KModes [54][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "", "Pactor 1", "", "", "Pactor 2", "", "Pactor 3", "", "", "Pactor 4", // 10 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500"}; +#endif + + + + + +FILE * LogHandle[4] = {NULL, NULL, NULL, NULL}; + +time_t LastLogTime[4] = {0, 0, 0, 0}; + +char FilesNames[4][100] = {"", "", "", ""}; + +char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; + + +BOOL OpenLogfile(int Flags) +{ + UCHAR FN[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = fopen(FN, "ab"); + +#ifndef WIN32 + + if (strcmp(FN, &FilesNames[Flags][0])) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); + unlink(SYMLINK); + strcpy(&FilesNames[Flags][0], FN); + symlink(FN, SYMLINK); + } + +#endif + + return (LogHandle[Flags] != NULL); +} + +typedef int (WINAPI FAR *FARPROCX)(); + +extern FARPROCX pDllBPQTRACE; + +struct SEM LogSEM = {0, 0}; + +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) +{ + char CRLF[2] = {0x0d,0x0a}; + struct tm * tm; + char Stamp[20]; + time_t LT; +// struct _EXCEPTION_POINTERS exinfo; + + // Write to Node BPQTRACE system + + if ((Flags == LOG_BBS || Flags == LOG_DEBUG_X) && MsgLen < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = 64; + Monframe.LENGTH = 12 + MsgLen; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], Msg, MsgLen); + Monframe.DEST[1 + MsgLen] = 0; + + time(&Monframe.Timestamp); +#ifdef LINBPQ + GetSemaphore(&Semaphore, 88); + BPQTRACE(&Monframe, FALSE); + FreeSemaphore(&Semaphore); +#else + if (pDllBPQTRACE) + pDllBPQTRACE(&Monframe, FALSE); +#endif + } +#ifndef LINBPQ + __try + { +#endif + + + +#ifndef LINBPQ + + if (hMonitor) + { + if (Flags == LOG_TCP && MonTCP) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_CHAT && MonCHAT) + { + WritetoMonitorWindow((char *)&Flag, 1); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + if (Msg[MsgLen-1] != '\r') + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_BBS && MonBBS) + { + WritetoMonitorWindow((char *)&Flag, 1); + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_DEBUG_X) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + } +#endif + + if (Flags == LOG_TCP && !LogTCP) + return; + if (Flags == LOG_BBS && !LogBBS) + return; + if (Flags == LOG_CHAT && !LogCHAT) + return; + + GetSemaphore(&LogSEM, 0); + + if (LogHandle[Flags] == NULL) + OpenLogfile(Flags); + + if (LogHandle[Flags] == NULL) + { + FreeSemaphore(&LogSEM); + return; + } + LT = time(NULL); + tm = gmtime(<); + + sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); + + fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + fwrite(call, 1, 10, LogHandle[Flags]); + } + else + fwrite(" ", 1, 10, LogHandle[Flags]); + + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + + if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') + fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); + else + fwrite(CRLF, 1, 2, LogHandle[Flags]); + + // Don't close/reopen logs every time + +// if ((LT - LastLogTime[Flags]) > 60) + { + LastLogTime[Flags] = LT; + fclose(LogHandle[Flags]); + LogHandle[Flags] = NULL; + } + FreeSemaphore(&LogSEM); + +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#endif +} + +int CriticalErrorHandler(char * error) +{ + Debugprintf("Critical Error %s", error); + ProgramErrors = 25; + CheckProgramErrors(); // Force close + return 0; +} + +BOOL CheckForTooManyErrors(ConnectionInfo * conn) +{ + conn->ErrorCount++; + + if (conn->ErrorCount > 4) + { + BBSputs(conn, "Too many errors - closing\r"); + conn->CloseAfterFlush = 20; + return TRUE; + } + return FALSE; +} + + + + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[16384]; + va_list(arglist); + int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); +#ifndef LINBPQ + WriteLogLine(NULL, '!',Mess, Len, LOG_DEBUG_X); +#endif + // #ifdef _DEBUG + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + +// #endif + return; +} + +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); + WriteLogLine(conn, InOut, Mess, Len, LogMode); + + return; +} + +struct MsgInfo * GetMsgFromNumber(int msgno) +{ + if (msgno < 1 || msgno > 999999) + return NULL; + + return MsgnotoMsg[msgno]; +} + +struct UserInfo * AllocateUserRecord(char * Call) +{ + struct UserInfo * User = zalloc(sizeof (struct UserInfo)); + + strcpy(User->Call, Call); + User->Length = sizeof (struct UserInfo); + + GetSemaphore(&AllocSemaphore, 0); + + UserRecPtr=realloc(UserRecPtr,(++NumberofUsers+1) * sizeof(void *)); + UserRecPtr[NumberofUsers]= User; + + FreeSemaphore(&AllocSemaphore); + + return User; +} + +struct MsgInfo * AllocateMsgRecord() +{ + struct MsgInfo * Msg = zalloc(sizeof (struct MsgInfo)); + + GetSemaphore(&AllocSemaphore, 0); + + MsgHddrPtr=realloc(MsgHddrPtr,(++NumberofMessages+1) * sizeof(void *)); + MsgHddrPtr[NumberofMessages] = Msg; + + FreeSemaphore(&AllocSemaphore); + + return Msg; +} + +BIDRec * AllocateBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + BIDRecPtr = realloc(BIDRecPtr,(++NumberofBIDs+1) * sizeof(void *)); + BIDRecPtr[NumberofBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +BIDRec * AllocateTempBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + TempBIDRecPtr=realloc(TempBIDRecPtr,(++NumberofTempBIDs+1) * sizeof(void *)); + TempBIDRecPtr[NumberofTempBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +struct UserInfo * LookupCall(char * Call) +{ + struct UserInfo * ptr = NULL; + int i; + + for (i=1; i <= NumberofUsers; i++) + { + ptr = UserRecPtr[i]; + + if (_stricmp(ptr->Call, Call) == 0) return ptr; + + } + + return NULL; +} + +int GetNetInt(char * Line) +{ + char temp[1024]; + char * ptr = strlop(Line, ','); + int n = atoi(Line); + if (ptr == NULL) + Line[0] = 0; + else + { + strcpy(temp, ptr); + strcpy(Line, temp); + } + return n; +} + +VOID GetUserDatabase() +{ + struct UserInfo UserRec; + + FILE * Handle; + size_t ReadLen; + struct UserInfo * user; + time_t UserLimit = time(NULL) - (UserLifetime * 86400); // Oldest user to keep + int i; + + // See if user config is in main config + + group = config_lookup (&cfg, "BBSUsers"); + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * stats; + struct MsgStats * Stats; + char * ptr, * ptr2; + + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + UserRecPtr = malloc(sizeof(void *)); + UserRecPtr[0] = malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + while (entry) + { + char call[16]; + + // entry->name is call, will have * in front if a call stating woth number + + if (entry->name[0] == '*') + strcpy(call, &entry->name[1]); + else + strcpy(call, entry->name); + + user = AllocateUserRecord(call); + + ptr = entry->value.sval; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Name, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Address, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->HomeBBS, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->QRA, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->pass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->ZIP, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->CMSPass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->lastmsg = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->flags = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->PageLen = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->BBSNumber = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->RMSSSIDBits = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->WebSeqNo = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->TimeLastConnected = atol(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) Stats = &user->Total; + stats = ptr; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + Stats = &user->Last; + stats = ptr2; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + + if ((user->flags & F_BBS) == 0) // Not BBS - Check Age + { + if (UserLifetime && user->TimeLastConnected) // Dont delete manually added Users that havent yet connected + { + if (user->TimeLastConnected < UserLimit) + { + // Too Old - ignore + + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + } + } + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + + entry = config_setting_get_elem (group, index++); + } + } + else + { + Handle = fopen(UserDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&UserRec, 0, sizeof (struct UserInfo)); + UserRec.Length = sizeof (struct UserInfo); + } + else + { + // See if format has changed + + if (UserRec.Length == 0) + { + // Old format without a Length field + + struct OldUserInfo * OldRec = (struct OldUserInfo *)&UserRec; + int Users = OldRec->ConnectsIn; // User Count in control record + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak + + Handle = fopen(UserDatabasePath, "rb"); + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); // Skip Control Record + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +OldNext: + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); + + if (ReadLen > 0) + { + if (OldRec->Call[0] < '0') + goto OldNext; // Blank record + + user = AllocateUserRecord(OldRec->Call); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + // Copy info from Old record + + user->lastmsg = OldRec->lastmsg; + user->Total.ConnectsIn = OldRec->ConnectsIn; + user->TimeLastConnected = OldRec->TimeLastConnected; + user->flags = OldRec->flags; + user->PageLen = OldRec->PageLen; + user->BBSNumber = OldRec->BBSNumber; + memcpy(user->Name, OldRec->Name, 18); + memcpy(user->Address, OldRec->Address, 61); + user->Total.MsgsReceived[0] = OldRec->MsgsReceived; + user->Total.MsgsSent[0] = OldRec->MsgsSent; + user->Total.MsgsRejectedIn[0] = OldRec->MsgsRejectedIn; // Messages we reject + user->Total.MsgsRejectedOut[0] = OldRec->MsgsRejectedOut; // Messages Rejectd by other end + user->Total.BytesForwardedIn[0] = OldRec->BytesForwardedIn; + user->Total.BytesForwardedOut[0] = OldRec->BytesForwardedOut; + user->Total.ConnectsOut = OldRec->ConnectsOut; // Forwarding Connects Out + user->RMSSSIDBits = OldRec->RMSSSIDBits; // SSID's to poll in RMS + memcpy(user->HomeBBS, OldRec->HomeBBS, 41); + memcpy(user->QRA, OldRec->QRA, 7); + memcpy(user->pass, OldRec->pass, 13); + memcpy(user->ZIP, OldRec->ZIP, 9); + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Defined as BBS - allocate and initialise forwarding structure + + // Add to BBS Chain; + + user->BBSNext = BBSChain; + BBSChain = user; + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) HighestBBSNumber = user->BBSNumber; + } + goto OldNext; + } + + SortBBSChain(); + fclose(Handle); + + return; + } + } + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +Next: + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen > 0) + { + if (UserRec.Call[0] < '0') + goto Next; // Blank record + + if (UserRec.TimeLastConnected == 0) + UserRec.TimeLastConnected = UserRec.xTimeLastConnected; + + if ((UserRec.flags & F_BBS) == 0) // Not BBS - Check Age + if (UserLifetime) // if limit set + if (UserRec.TimeLastConnected) // Dont delete manually added Users that havent yet connected + if (UserRec.TimeLastConnected < UserLimit) + goto Next; // Too Old - ignore + + user = AllocateUserRecord(UserRec.Call); + memcpy(user, &UserRec, sizeof (UserRec)); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + user->ForwardingInfo = NULL; // In case left behind on crash + user->BBSNext = NULL; + user->POP3Locked = FALSE; + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + goto Next; + } + fclose(Handle); + } + + // Setting up BBS struct has been moved until all user record + // have been read so we can fix corrupt BBSNUmber + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Add to BBS Chain; + + if (user->BBSNumber == NBBBS) // Fix corrupt records + { + user->BBSNumber = FindFreeBBSNumber(); + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + } + + user->BBSNext = BBSChain; + BBSChain = user; + +// Logprintf(LOG_BBS, NULL, '?', "BBS %s BBSNumber %d", user->Call, user->BBSNumber); + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) + HighestBBSNumber = user->BBSNumber; + } + } + + // Check for dulicate BBS numbers + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_BBS) + { + if (user->BBSNumber == 0) + user->BBSNumber = FindFreeBBSNumber(); + + CheckBBSNumber(user->BBSNumber); + } + } + + SortBBSChain(); +} + +VOID CopyUserDatabase() +{ + return; // User config now in main config file +/* + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak +*/ +} + +VOID CopyConfigFile(char * ConfigName) +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); // Move .bak to .bak.1 + + CopyFile(ConfigName, Backup1, FALSE); // Copy to .bak +} + + + +VOID SaveUserDatabase() +{ + SaveConfig(ConfigName); // User config is now in main config file + GetConfig(ConfigName); + +/* + FILE * Handle; + size_t WriteLen; + int i; + + Handle = fopen(UserDatabasePath, "wb"); + + UserRecPtr[0]->Total.ConnectsIn = NumberofUsers; + + for (i=0; i <= NumberofUsers; i++) + { + WriteLen = fwrite(UserRecPtr[i], 1, (int)sizeof (struct UserInfo), Handle); + } + + fclose(Handle); +*/ + return; +} + +VOID GetMessageDatabase() +{ + struct MsgInfo MsgRec; + FILE * Handle; + size_t ReadLen; + struct MsgInfo * Msg; + char * MsgBytes; + int FileRecsize = sizeof(struct MsgInfo); // May be changed if reformating + BOOL Reformatting = FALSE; + char HEX[3] = ""; + int n; + + // See if Message Database is in main config + + group = config_lookup (&cfg, "MSGS"); + +// group = 0; + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * ptr, * ptr2; + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + if (entry) + { + // First Record has current message number + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + ptr2 = strlop(ptr2, '|'); + if (ptr2) + LatestMsg = atoi(ptr2); + } + + entry = config_setting_get_elem (group, index++); + + while (entry) + { + // entry->name is MsgNo with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + memset(&MsgRec, 0, sizeof(struct MsgInfo)); + + MsgRec.number = atoi(&entry->name[1]); + MsgRec.type = ptr[0]; + + ptr = ptr2; + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + ptr2 = strlop(ptr, '|'); + MsgRec.status = ptr[0]; + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.length = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datereceived = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bbsfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.via, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.from, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.to, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bid, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.B2Flags = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datecreated = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datechanged = atol(ptr); + + ptr = ptr2; + if (ptr) ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.fbbs[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.forw[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.emailfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.UTF8 = atoi(ptr); + + ptr = ptr2; + + if (ptr) + { + strcpy(MsgRec.title, ptr); + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + free(MsgBytes); + Msg = AllocateMsgRecord(); + memcpy(Msg, &MsgRec, sizeof (MsgRec)); + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + BuildNNTPList(Msg); // Build NNTP Groups list + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + } + } + entry = config_setting_get_elem (group, index++); + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + return; + } + + Handle = fopen(MsgDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + return; + } + + // Get First Record + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&MsgRec, 0, sizeof (MsgRec)); + MsgRec.status = 2; + } + + // Set up control record + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= malloc(sizeof (MsgRec)); + memcpy(MsgHddrPtr[0], &MsgRec, sizeof (MsgRec)); + + LatestMsg=MsgHddrPtr[0]->length; + + NumberofMessages = 0; + + if (MsgRec.status == 1) // Used as file format version + // 0 = original, 1 = Extra email from addr, 2 = More BBS's. + { + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(MsgDatabasePath, Backup1, FALSE); // Copy to .oldformat + + Handle = fopen(MsgDatabasePath, "rb"); + + FileRecsize = sizeof(struct OldMsgInfo); + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + MsgHddrPtr[0]->status = 2; + } + +Next: + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen > 0) + { + // Validate Header + + if (FileRecsize == sizeof(struct MsgInfo)) + { + if (MsgRec.type == 0 || MsgRec.number == 0) + goto Next; + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + // MsgRec.length = strlen(MsgBytes); + free(MsgBytes); + } + else + goto Next; + + Msg = AllocateMsgRecord(); + + memcpy(Msg, &MsgRec, +sizeof (MsgRec)); + } + else + { + // Resizing - record from file is an OldRecInfo + + struct OldMsgInfo * OldMessage = (struct OldMsgInfo *) &MsgRec; + + if (OldMessage->type == 0) + goto Next; + + if (OldMessage->number > 99999 || OldMessage->number < 1) + goto Next; + + Msg = AllocateMsgRecord(); + + + Msg->B2Flags = OldMessage->B2Flags; + memcpy(Msg->bbsfrom, OldMessage->bbsfrom, 7); + memcpy(Msg->bid, OldMessage->bid, 13); + Msg->datechanged = OldMessage->datechanged; + Msg->datecreated = OldMessage->datecreated; + Msg->datereceived = OldMessage->datereceived; + memcpy(Msg->emailfrom, OldMessage->emailfrom, 41); + memcpy(Msg->fbbs , OldMessage->fbbs, 10); + memcpy(Msg->forw , OldMessage->forw, 10); + memcpy(Msg->from, OldMessage->from, 7); + Msg->length = OldMessage->length; + Msg->nntpnum = OldMessage->nntpnum; + Msg->number = OldMessage->number; + Msg->status = OldMessage->status; + memcpy(Msg->title, OldMessage->title, 61); + memcpy(Msg->to, OldMessage->to, 7); + Msg->type = OldMessage->type; + memcpy(Msg->via, OldMessage->via, 41); + } + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + // Move Dates if first run with new format + + if (Msg->datecreated == 0) + Msg->datecreated = Msg->xdatecreated; + + if (Msg->datereceived == 0) + Msg->datereceived = Msg->xdatereceived; + + if (Msg->datechanged == 0) + Msg->datechanged = Msg->xdatechanged; + + BuildNNTPList(Msg); // Build NNTP Groups list + + Msg->Locked = 0; // In case left locked + Msg->Defered = 0; // In case left set. + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + + goto Next; + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + fclose(Handle); +} + +VOID CopyMessageDatabase() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + +// return; + + // Keep 4 Generations + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak"); + + CopyFile(MsgDatabasePath, Backup2, FALSE); // Copy to .bak + +} + +VOID SaveMessageDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + char Key[16]; + struct MsgInfo *Msg; +// char CfgName[MAX_PATH]; + char HEXString1[64]; + char HEXString2[64]; + int n; +// char * CfgBuffer; + char Cfg[1024]; +// int CfgLen = 0; +// FILE * hFile; + +// SaveConfig(ConfigName); // Message Headers now in main config +// return; + +#ifdef LINBPQ + RefreshWebMailIndex(); +#else + if (pRefreshWebMailIndex) + pRefreshWebMailIndex(); +#endif + + Handle = fopen(MsgDatabasePath, "wb"); + + if (Handle == NULL) + { + CriticalErrorHandler("Failed to open message database"); + return; + } + + MsgHddrPtr[0]->status = 2; + MsgHddrPtr[0]->number = NumberofMessages; + MsgHddrPtr[0]->length = LatestMsg; + + for (i=0; i <= NumberofMessages; i++) + { + WriteLen = fwrite(MsgHddrPtr[i], 1, sizeof (struct MsgInfo), Handle); + + if (WriteLen != sizeof(struct MsgInfo)) + { + CriticalErrorHandler("Failed to write message database record"); + return; + } + } + + if (fclose(Handle) != 0) + CriticalErrorHandler("Failed to close message database"); + + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d:\r\n", i); + + n = sprintf(Cfg, "%c|%c|%d|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->number, Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + } + + return; +} + +VOID GetBIDDatabase() +{ + BIDRec BIDRec; + FILE * Handle; + size_t ReadLen; + BIDRecP BID; + int index = 0; + char * ptr, * ptr2; + + // If BID info is in main config file, use it + + group = config_lookup (&cfg, "BIDS"); + + if (group) + { + config_setting_t * entry = config_setting_get_elem (group, index++); + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + while (entry) + { + // entry->name is Bid with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + if (ptr && ptr2) + { + BID = AllocateBIDRecord(); + strcpy(BID->BID, &entry->name[1]); + BID->mode = atoi(ptr); + BID->u.timestamp = atoi(ptr2); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + } + entry = config_setting_get_elem (group, index++); + } + return; + } + + Handle = fopen(BIDDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&BIDRec, 0, sizeof (BIDRec)); + } + + // Set up control record + + BIDRecPtr = malloc(sizeof(void *)); + BIDRecPtr[0] = malloc(sizeof (BIDRec)); + memcpy(BIDRecPtr[0], &BIDRec, sizeof (BIDRec)); + + NumberofBIDs = 0; + +Next: + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen > 0) + { + BID = AllocateBIDRecord(); + memcpy(BID, &BIDRec, sizeof (BIDRec)); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + goto Next; + } + + fclose(Handle); +} + +VOID CopyBIDDatabase() +{ + char Backup[MAX_PATH]; + +// return; + + + strcpy(Backup, BIDDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(BIDDatabasePath, Backup, FALSE); +} + +VOID SaveBIDDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + +// return; // Bids are now in main config and are saved when message is saved + + Handle = fopen(BIDDatabasePath, "wb"); + + BIDRecPtr[0]->u.msgno = NumberofBIDs; // First Record has file size + + for (i=0; i <= NumberofBIDs; i++) + { + WriteLen = fwrite(BIDRecPtr[i], 1, sizeof (BIDRec), Handle); + } + + fclose(Handle); + + return; +} + +BIDRec * LookupBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofBIDs; i++) + { + ptr = BIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) + return ptr; + } + + return NULL; +} + +BIDRec * LookupTempBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofTempBIDs; i++) + { + ptr = TempBIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) return ptr; + } + + return NULL; +} + +VOID RemoveTempBIDS(CIRCUIT * conn) +{ + // Remove any Temp BID records for conn. Called when connection closes - Msgs will be complete or failed + + if (NumberofTempBIDs == 0) + return; + else + { + BIDRec * ptr = NULL; + BIDRec ** NewTempBIDRecPtr = zalloc((NumberofTempBIDs+1) * sizeof(void *)); + int i = 0, n; + + GetSemaphore(&AllocSemaphore, 0); + + for (n = 1; n <= NumberofTempBIDs; n++) + { + ptr = TempBIDRecPtr[n]; + + if (ptr) + { + if (ptr->u.conn == conn) + // Remove this entry + free(ptr); + else + NewTempBIDRecPtr[++i] = ptr; + } + } + + NumberofTempBIDs = i; + + free(TempBIDRecPtr); + + TempBIDRecPtr = NewTempBIDRecPtr; + FreeSemaphore(&AllocSemaphore); + } + +} + +VOID GetBadWordFile() +{ + FILE * Handle; + DWORD FileSize; + char * ptr1, * ptr2; + struct stat STAT; + + if (stat(BadWordsPath, &STAT) == -1) + return; + + FileSize = STAT.st_size; + + Handle = fopen(BadWordsPath, "rb"); + + if (Handle == NULL) + return; + + // Release old info in case a re-read + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + BadWords = NULL; + BadFile = NULL; + NumberofBadWords = 0; + + BadFile = malloc(FileSize+1); + + fread(BadFile, 1, FileSize, Handle); + + fclose(Handle); + + BadFile[FileSize]=0; + + _strlwr(BadFile); // Compares are case-insensitive + + ptr1 = BadFile; + + while (ptr1) + { + if (*ptr1 == '\n') ptr1++; + + ptr2 = strtok_s(NULL, "\r\n", &ptr1); + if (ptr2) + { + if (*ptr2 != '#') + { + BadWords = realloc(BadWords,(++NumberofBadWords+1) * sizeof(void *)); + BadWords[NumberofBadWords] = ptr2; + } + } + else + break; + } +} + +BOOL CheckBadWord(char * Word, char * Msg) +{ + char * ptr1 = Msg, * ptr2; + size_t len = strlen(Word); + + while (*ptr1) // Stop at end + { + ptr2 = strstr(ptr1, Word); + + if (ptr2 == NULL) + return FALSE; // OK + + // Only bad if it ia not part of a longer word + + if ((ptr2 == Msg) || !(isalpha(*(ptr2 - 1)))) // No alpha before + if (!(isalpha(*(ptr2 + len)))) // No alpha after + return TRUE; // Bad word + + // Keep searching + + ptr1 = ptr2 + len; + } + + return FALSE; // OK +} + +BOOL CheckBadWords(char * Msg) +{ + char * dupMsg = _strlwr(_strdup(Msg)); + int i; + + for (i = 1; i <= NumberofBadWords; i++) + { + if (CheckBadWord(BadWords[i], dupMsg)) + { + free(dupMsg); + return TRUE; // Bad + } + } + + free(dupMsg); + return FALSE; // OK + +} + +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertWelcomeMsg, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewWelcomeMsg, LOG_BBS); + else + ExpandAndSendMessage(conn, WelcomeMsg, LOG_BBS); + + if (user->HomeBBS[0] == 0 && !DontNeedHomeBBS) + BBSputs(conn, "Please enter your Home BBS using the Home command.\rYou may also enter your QTH and ZIP/Postcode using qth and zip commands.\r"); + +// if (user->flags & F_Temp_B2_BBS) +// nodeprintf(conn, "%s CMS >\r", BBSName); +// else + SendPrompt(conn, user); +} + +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->Temp->ListSuspended) + return; // Dont send prompt if pausing a listing + + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertPrompt, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewPrompt, LOG_BBS); + else + ExpandAndSendMessage(conn, Prompt, LOG_BBS); + +// if (user->flags & F_Expert) +// nodeprintf(conn, "%s\r", ExpertPrompt); +// else if (conn->NewUser) +// nodeprintf(conn, "%s\r", NewPrompt); +// else +// nodeprintf(conn, "%s\r", Prompt); +} + + + +VOID * _zalloc(size_t len) +{ + // ?? malloc and clear + + void * ptr; + + ptr=malloc(len); + memset(ptr, 0, len); + + return ptr; +} + +BOOL isAMPRMsg(char * Addr) +{ + // See if message is addressed to ampr.org and is either + // for us or we have SendAMPRDirect (ie don't need RMS or SMTP to send it) + + size_t toLen = strlen(Addr); + + if (_memicmp(&Addr[toLen - 8], "ampr.org", 8) == 0) + { + // message is for ampr.org + + char toCall[48]; + char * via; + + strcpy(toCall, _strupr(Addr)); + + via = strlop(toCall, '@'); + + if (_stricmp(via, AMPRDomain) == 0) + { + // message is for us. + + return TRUE; + } + + if (SendAMPRDirect) + { + // We want to send ampr mail direct to host. Queue to BBS AMPR + + if (FindAMPR()) + { + // We have bbs AMPR + + return TRUE; + } + } + } + return FALSE; +} + +struct UserInfo * FindAMPR() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "AMPR") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindRMS() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "RMS") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindBBS(char * Name) +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, Name) == 0) + return bbs; + } + + return NULL; +} + +int CountConnectionsOnPort(int CheckPort) +{ + int n, Count = 0; + CIRCUIT * conn; + int port, sesstype, paclen, maxframe, l4window; + char callsign[11]; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active) + { + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + if (port == CheckPort) + Count++; + } + } + + return Count; +} + + +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type) +{ + char ** Calls; + + if (Type == 'B' && FilterWPBulls && _stricmp(To, "WP") == 0) + return TRUE; + + if (RejFrom && From) + { + Calls = RejFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (RejTo && To) + { + Calls = RejTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (RejAt && ATBBS) + { + Calls = RejAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (RejBID && BID) + { + Calls = RejBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0]) == 0) + return TRUE; + } + + Calls++; + } + } + return FALSE; // Ok to accept +} + +BOOL CheckValidCall(char * From) +{ + unsigned int i; + + if (DontCheckFromCall) + return TRUE; + + if (strcmp(From, "SYSOP") == 0 || strcmp(From, "SYSTEM") == 0 || + strcmp(From, "IMPORT") == 0 || strcmp(From, "SMTP:") == 0 || strcmp(From, "RMS:") == 0) + return TRUE; + + for (i = 1; i < strlen(From); i++) // skip first which may also be digit + { + if (isdigit(From[i])) + { + // Has a digit. Check Last is not digit + + if (isalpha(From[strlen(From) - 1])) + return TRUE; + } + } + + // No digit, return false + + return FALSE; +} + +BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID) +{ + char ** Calls; + + if (HoldFrom && From) + { + Calls = HoldFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldTo && To) + { + Calls = HoldTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldAt && ATBBS) + { + Calls = HoldAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldBID && BID) + { + Calls = HoldBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0]) == 0) + return TRUE; + } + + Calls++; + } + } + return FALSE; // Ok to accept +} + +BOOL CheckifLocalRMSUser(char * FullTo) +{ + struct UserInfo * user = LookupCall(FullTo); + + if (user) + if (user->flags & F_POLLRMS) + return TRUE; + + return FALSE; + +} + + + +int check_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + return (mask[(bbsnumber - 1) / 8] & (1 << ((bbsnumber - 1) % 8))); + else + return 0; +} + + +void set_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] |= (1 << ((bbsnumber - 1) % 8)); +} + + +void clear_fwd_bit (char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] &= (~(1 << ((bbsnumber - 1) % 8))); +} + +VOID BBSputs(CIRCUIT * conn, char * buf) +{ + // Sends to user and logs + + WriteLogLine(conn, '>',buf, (int)strlen(buf) -1, LOG_BBS); + + QueueMsg(conn, buf, (int)strlen(buf)); +} + +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + return; +} + +// nodeprintfEx add a LF if NEEFLF is set + +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + if (conn->BBSFlags & NEEDLF) + QueueMsg(conn, "\r", 1); + + return; +} + + +int compare( const void *arg1, const void *arg2 ); + +VOID SortBBSChain() +{ + struct UserInfo * user; + struct UserInfo * users[161]; + int i = 0, n; + + // Get array of addresses + + for (user = BBSChain; user; user = user->BBSNext) + { + users[i++] = user; + if (i > 160) break; + } + + qsort((void *)users, i, sizeof(void *), compare ); + + BBSChain = NULL; + + // Rechain (backwards, as entries ate put on front of chain) + + for (n = i-1; n >= 0; n--) + { + users[n]->BBSNext = BBSChain; + BBSChain = users[n]; + } +} + +int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + +int CountMessagesTo(struct UserInfo * user, int * Unread) +{ + int i, Msgs = 0; + UCHAR * Call = user->Call; + + *Unread = 0; + + for (i = NumberofMessages; i > 0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (_stricmp(MsgHddrPtr[i]->to, Call) == 0) + { + Msgs++; + if (MsgHddrPtr[i]->status == 'N') + *Unread = *Unread + 1; + } + } + return(Msgs); +} + + + +// Custimised message handling routines. +/* + Variables - a subset of those used by FBB + + $C : Number of the next message. + $I : First name of the connected user. + $L : Number of the latest message. + $N : Number of active messages + $U : Callsign of the connected user. + $W : Inserts a carriage return. + $Z : Last message read by the user (L command). + %X : Number of messages for the user. + %x : Number of new messages for the user. +*/ + +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + size_t len; + char Dollar[] = "$"; + char CR[] = "\r"; + char num[20]; + int Msgs = 0, Unread = 0; + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = ptr - OldP; // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'I': // First name of the connected user. + + pptr = conn->UserPointer->Name; + break; + + case 'L': // Number of the latest message. + + sprintf(num, "%d", LatestMsg); + pptr = num; + break; + + case 'N': // Number of active messages. + + sprintf(num, "%d", NumberofMessages); + pptr = num; + break; + + case 'U': // Callsign of the connected user. + + pptr = conn->UserPointer->Call; + break; + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + case 'Z': // Last message read by the user (L command). + + sprintf(num, "%d", conn->UserPointer->lastmsg); + pptr = num; + break; + + case 'X': // Number of messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + case 'x': // Number of new messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Unread); + pptr = num; + break; + + case 'F': // Number of new messages to forward to this BBS. + + Msgs = CountMessagestoForward(conn->UserPointer); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, (int)strlen(NewMessage)); + + WriteLogLine(conn, '>', NewMessage, (int)len, LOG); + QueueMsg(conn, NewMessage, (int)len); +} + +BOOL isdigits(char * string) +{ + // Returns TRUE id sting is decimal digits + + size_t i, n = strlen(string); + + for (i = 0; i < n; i++) + { + if (isdigit(string[i]) == FALSE) return FALSE; + } + return TRUE; +} + +BOOL wildcardcompare(char * Target, char * Match) +{ + // Do a compare with string *string string* *string* + + // Strings should all be UC + + char Pattern[100]; + char * firststar; + + strcpy(Pattern, Match); + firststar = strchr(Pattern,'*'); + + if (firststar) + { + size_t Len = strlen(Pattern); + + if (Pattern[0] == '*' && Pattern[Len - 1] == '*') // * at start and end + { + Pattern[Len - 1] = 0; + return !(strstr(Target, &Pattern[1]) == NULL); + } + if (Pattern[0] == '*') // * at start + { + // Compare the last len - 1 chars of Target + + size_t Targlen = strlen(Target); + size_t Comparelen = Targlen - (Len - 1); + + if (Len == 1) // Just * + return TRUE; + + if (Comparelen < 0) // Too Short + return FALSE; + + return (memcmp(&Target[Comparelen], &Pattern[1], Len - 1) == 0); + } + + // Must be * at end - compare first Len-1 char + + return (memcmp(Target, Pattern, Len - 1) == 0); + } + + // No WildCards - straight strcmp + return (strcmp(Target, Pattern) == 0); +} + +#ifndef LINBPQ + +PrintMessage(HDC hDC, struct MsgInfo * Msg); + +PrintMessages(HWND hDlg, int Count, int * Indexes) +{ + int i, CurrentMsgIndex; + char MsgnoText[10]; + int Msgno; + struct MsgInfo * Msg; + int Len = MAX_PATH; + BOOL hResult; + PRINTDLG pdx = {0}; + HDC hDC; + +// CHOOSEFONT cf; + LOGFONT lf; + HFONT hFont; + + + // Initialize the PRINTDLG structure. + + pdx.lStructSize = sizeof(PRINTDLG); + pdx.hwndOwner = hWnd; + pdx.hDevMode = NULL; + pdx.hDevNames = NULL; + pdx.hDC = NULL; + pdx.Flags = PD_RETURNDC | PD_COLLATE; + pdx.nMinPage = 1; + pdx.nMaxPage = 1000; + pdx.nCopies = 1; + pdx.hInstance = 0; + pdx.lpPrintTemplateName = NULL; + + // Invoke the Print property sheet. + + hResult = PrintDlg(&pdx); + + memset(&lf, 0, sizeof(LOGFONT)); + + /* + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = pdx.hDC; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_PRINTERFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = PRINTER_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. +*/ + + lf.lfHeight = -56; + lf.lfWeight = 600; + lf.lfOutPrecision = 3; + lf.lfClipPrecision = 2; + lf.lfQuality = 1; + lf.lfPitchAndFamily = '1'; + strcpy (lf.lfFaceName, "Courier New"); + + hFont = CreateFontIndirect(&lf); + + if (hResult) + { + // User clicked the Print button, so use the DC and other information returned in the + // PRINTDLG structure to print the document. + + DOCINFO pdi; + + pdi.cbSize = sizeof(DOCINFO); + pdi.lpszDocName = "BBS Message Print"; + pdi.lpszOutput = NULL; + pdi.lpszDatatype = "RAW"; + pdi.fwType = 0; + + hDC = pdx.hDC; + + SelectObject(hDC, hFont); + + StartDoc(hDC, &pdi); + StartPage(hDC); + + for (i = 0; i < Count; i++) + { + SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Indexes[i], (LPARAM)(LPCTSTR)&MsgnoText); + + Msgno = atoi(MsgnoText); + + for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) + { + Msg = MsgHddrPtr[CurrentMsgIndex]; + + if (Msg->number == Msgno) + { + PrintMessage(hDC, Msg); + break; + } + } + } + + EndDoc(hDC); + } + + if (pdx.hDevMode != NULL) + GlobalFree(pdx.hDevMode); + if (pdx.hDevNames != NULL) + GlobalFree(pdx.hDevNames); + + if (pdx.hDC != NULL) + DeleteDC(pdx.hDC); + + return 0; +} + +PrintMessage(HDC hDC, struct MsgInfo * Msg) +{ + int Len = MAX_PATH; + char * MsgBytes; + char * Save; + int Msglen; + + StartPage(hDC); + + Save = MsgBytes = ReadMessageFile(Msg->number); + + Msglen = Msg->length; + + if (MsgBytes) + { + char Hddr[1000]; + char FullTo[100]; + int HRes, VRes; + char * ptr1, * ptr2; + int LineLen; + + RECT Rect; + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MsgBytes, "Body:"); + if (ptr) + { + Msglen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + MsgBytes = ptr + 4; + } + + HRes = GetDeviceCaps(hDC, HORZRES) - 50; + VRes = GetDeviceCaps(hDC, VERTRES) - 50; + + Rect.top = 50; + Rect.left = 50; + Rect.right = HRes; + Rect.bottom = VRes; + + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_CALCRECT | DT_WORDBREAK); + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_WORDBREAK); + + // process message a line at a time. When page is full, output a page break + + ptr1 = MsgBytes; + ptr2 = ptr1; + + while (Msglen-- > 0) + { + if (*ptr1++ == '\r') + { + // Output this line + + // First check if it will fit + + Rect.top = Rect.bottom; + Rect.right = HRes; + Rect.bottom = VRes; + + LineLen = ptr1 - ptr2 - 1; + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + + if (Rect.bottom >= VRes) + { + EndPage(hDC); + StartPage(hDC); + + Rect.top = 50; + Rect.bottom = VRes; + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + } + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_WORDBREAK); + + if (*(ptr1) == '\n') + { + ptr1++; + Msglen--; + } + + ptr2 = ptr1; + } + } + + free(Save); + + EndPage(hDC); + + } + return 0; +} + +#endif + + +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup) +{ + char FileName[MAX_PATH] = "Messages.in"; + int Files = 0; + int WriteLen=0; + FILE *in; + CIRCUIT dummyconn; + struct UserInfo User; + int Index = 0; + + char Buffer[100000]; + char *buf = Buffer; + + if (FN[0]) // Name supplled + strcpy(FileName, FN); + + else + { +#ifndef LINBPQ + OPENFILENAME Ofn; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = MainWnd; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileName; + Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = BaseDir; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + Ofn.lpstrTitle = NULL;//; + + if (!GetOpenFileName(&Ofn)) + return 0; +#endif + } + + in = fopen(FileName, "rb"); + + if (!(in)) + { + char msg[500]; + sprintf_s(msg, sizeof(msg), "Failed to open %s", FileName); + if (conn) + nodeprintf(conn, "%s\r", msg); +#ifdef WIN32 + else + if (Nopopup == FALSE) + MessageBox(NULL, msg, "BPQMailChat", MB_OK); +#endif + return 0; + } + + memset(&dummyconn, 0, sizeof(CIRCUIT)); + memset(&User, 0, sizeof(struct UserInfo)); + + if (conn == 0) + { + conn = &dummyconn; + + dummyconn.UserPointer = &User; // Was SYSOPCall, but I think that is wrong. + strcpy(User.Call, "IMPORT"); + User.flags |= F_EMAIL; + dummyconn.sysop = TRUE; + dummyconn.BBSFlags = BBS; + + strcpy(dummyconn.Callsign, "IMPORT"); + } + + while(fgets(Buffer, 99999, in)) + { + // First line should start SP/SB ?ST? + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * Msg; + char To[100]= ""; + int msglen; + char * Context; + char * Arg1, * Cmd; + +NextMessage: + + From = NULL; + BID = NULL; + ATBBS = NULL; + To[0]= 0; + + Sleep(100); + + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (Buffer[0] == 0) //Blank Line + continue; + + WriteLogLine(conn, '>', Buffer, (int)strlen(Buffer), LOG_BBS); + + if (dummyconn.sysop == 0) + { + nodeprintf(conn, "%s\r", Buffer); + Flush(conn); + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + fclose(in); + return Files; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + if (dummyconn.sysop) + Debugprintf("Bad Import Line %s", Buffer); + else + nodeprintf(conn, "Bad Import Line %s\r", Buffer); + + fclose(in); + return Files; + } + + strcpy(To, Arg1); + + if (DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + { + if (CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL)) + { + Msg = conn->TempMsg; + + // SP is Ok, read message; + + ClearQueue(conn); + + fgets(Buffer, 99999, in); + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + if (strlen(Buffer) > 60) + Buffer[60] = 0; + + strcpy(Msg->title, Buffer); + + // Read the lines + + conn->Flags |= GETTINGMESSAGE; + + Buffer[0] = 0; + + fgets(Buffer, 99999, in); + + while ((conn->Flags & GETTINGMESSAGE) && Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + msglen = (int)strlen(Buffer); + Buffer[msglen++] = 13; + ProcessMsgLine(conn, conn->UserPointer,Buffer, msglen); + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + + // Message completed (or off end of file) + + Files ++; + + ClearQueue(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + else + { + fclose(in); + return Files; + } + } + else + { + // Create failed + + Flush(conn); + } + } + + // Search for next message + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + while (Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (_stricmp(Buffer, "/EX") == 0) + { + // Found end + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + } + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + } + + fclose(in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + return Files; +} +char * ReadMessageFileEx(struct MsgInfo * MsgRec) +{ + // Sets Message Size from File Size + + int msgno = MsgRec->number; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + MsgRec->length = FileSize; + + return MsgBytes; +} + +char * ReadMessageFile(int msgno) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes = malloc(FileSize + 100); // A bit of space for alias substitution on B2 + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + + return MsgBytes; +} + + +int QueueMsg(ConnectionInfo * conn, char * msg, int len) +{ + // Add Message to queue for this connection + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // Create or extend buffer + + GetSemaphore(&OutputSEM, 0); + + conn->OutputQueue=realloc(conn->OutputQueue, conn->OutputQueueLength + len); + + if (conn->OutputQueue == NULL) + { + // relloc failed - should never happen, but clean up + + CriticalErrorHandler("realloc failed to expand output queue"); + FreeSemaphore(&OutputSEM); + return 0; + } + + memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); + conn->OutputQueueLength += len; + FreeSemaphore(&OutputSEM); + + return len; +} + +void TrytoSend() +{ + // call Flush on any connected streams with queued data + + ConnectionInfo * conn; + struct ConsoleInfo * Cons; + + int n; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + Flush(conn); + + // if an FLARQ mail has been sent see if queues have cleared + + if (conn->BBSFlags & YAPPTX) + { + YAPPSendData(conn); + } + else if (conn->OutputQueue == NULL && (conn->BBSFlags & ARQMAILACK)) + { + int n = TXCount(conn->BPQStream); // All Sent and Acked? + + if (n == 0) + { + struct MsgInfo * Msg = conn->FwdMsg; + + conn->ARQClearCount--; + + if (conn->ARQClearCount <= 0) + { + Logprintf(LOG_BBS, conn, '>', "ARQ Send Complete"); + + // Mark mail as sent, and look for more + + clear_fwd_bit(Msg->fbbs, conn->UserPointer->BBSNumber); + set_fwd_bit(Msg->forw, conn->UserPointer->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) + { + Msg->status = 'F'; // Mark as forwarded + Msg->datechanged=time(NULL); + } + + conn->BBSFlags &= ~ARQMAILACK; + conn->UserPointer->ForwardingInfo->MsgCount--; + + SaveMessageDatabase(); + SendARQMail(conn); // See if any more - close if not + } + } + else + conn->ARQClearCount = 10; + } + } + } +#ifndef LINBPQ + for (Cons = ConsHeader[0]; Cons; Cons = Cons->next) + { + if (Cons->Console) + Flush(Cons->Console); + } +#endif +} + + +void Flush(CIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + if (conn->OutputQueue == NULL) + { + // Nothing to send. If Close after Flush is set, disconnect + + if (conn->CloseAfterFlush) + { + conn->CloseAfterFlush--; + + if (conn->CloseAfterFlush) + return; + + Disconnect(conn->BPQStream); + conn->ErrorCount = 0; + } + + return; // Nothing to send + } + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent=0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 15) + return; // Busy + + if (conn->BBSFlags & SYSOPCHAT) // Suspend queued output while sysop chatting + return; + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + return; + + if (tosend <= conn->paclen) + len=tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + if (conn->Paging) + { + // look for CR chars in message to send. Increment LinesSent, and stop if at limit + + UCHAR * ptr1 = &conn->OutputQueue[conn->OutputGetPointer]; + UCHAR * ptr2; + int lenleft = len; + + ptr2 = memchr(ptr1, 0x0d, len); + + while (ptr2) + { + conn->LinesSent++; + ptr2++; + lenleft = len - (int)(ptr2 - ptr1); + + if (conn->LinesSent >= conn->PageLen) + { + len = (int)(ptr2 - &conn->OutputQueue[conn->OutputGetPointer]); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + conn->OutputGetPointer+=len; + tosend-=len; + SendUnbuffered(conn->BPQStream, "bort, Continue..>", 25); + FreeSemaphore(&OutputSEM); + return; + + } + ptr2 = memchr(ptr2, 0x0d, lenleft); + } + } + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer+=len; + + FreeSemaphore(&OutputSEM); + + tosend-=len; + sent++; + + if (sent > 15) + return; + } + + // All Sent. Free buffers and reset pointers + + conn->LinesSent = 0; + + ClearQueue(conn); +} + +VOID ClearQueue(ConnectionInfo * conn) +{ + if (conn->OutputQueue == NULL) + return; + + GetSemaphore(&OutputSEM, 0); + + free(conn->OutputQueue); + + conn->OutputQueue=NULL; + conn->OutputGetPointer=0; + conn->OutputQueueLength=0; + + FreeSemaphore(&OutputSEM); +} + + + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB) +{ + struct UserInfo * user; + + Msg->status='K'; + Msg->datechanged=time(NULL); + + // Remove any forwarding references + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount--; + clear_fwd_bit(Msg->fbbs, user->BBSNumber); + } + } + } + if (SaveDB) + SaveMessageDatabase(); + RebuildNNTPList(); +} + +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + struct MsgInfo * Msg; + + while (Arg1) + { + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + goto Next; + } + + if (Msg->type != 'T') + { + nodeprintf(conn, "Message %d not an NTS Message\r", msgno); + goto Next; + } + + if (Msg->status == 'N') + nodeprintf(conn, "Warning - Message has status N\r"); + + Msg->status = 'D'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + + nodeprintf(conn, "Message #%d Flagged as Delivered\r", msgno); + Next: + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + // Param is either ALL or a list of numbers + + if (Arg1 == NULL) + { + nodeprintf(conn, "No message number\r"); + return; + } + + if (_stricmp(Arg1, "ALL") == 0) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", Msg->number); + } + } + return; + } + + while (Arg1) + { + msgno = atoi(Arg1); + Msg = GetMsgFromNumber(msgno); + + if (Msg) + { + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", msgno); + } + else + { + nodeprintf(conn, "Message #%d was not held\r", msgno); + } + } + else + nodeprintf(conn, "Message #%d not found\r", msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoKillCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + switch (toupper(Cmd[1])) + { + + case 0: // Just K + + while (Arg1) + { + msgno = atoi(Arg1); + KillMessage(conn, user, msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + SaveMessageDatabase(); + return; + + case 'M': // Kill Mine + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + { + if (Msg->type == 'P' && Msg->status == 'Y') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + + SaveMessageDatabase(); + return; + + case 'H': // Kill Held + + if (conn->sysop) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + SaveMessageDatabase(); + return; + + case '>': // K> - Kill to + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesTo(conn, user, Arg1) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + } + + case '<': + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesFrom(conn, user, Arg1) == 0); + BBSputs(conn, "No Messages found\r"); + + return; + } + } + + nodeprintf(conn, "*** Error: Invalid Kill option %c\r", Cmd[1]); + + return; + +} + +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->to, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->from, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg) +{ + if (SYSOP || (Msg->type == 'T' && UserCantKillT == FALSE)) + return TRUE; + + if (Msg->type == 'P') + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + + if (Msg->type == 'B') + if (_stricmp(Msg->from, Call) == 0) + return TRUE; + + return FALSE; +} + +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL || Msg->status == 'K') + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (OkToKillMessage(conn->sysop, user->Call, Msg)) + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", msgno); + } + else + nodeprintf(conn, "Not your message\r"); +} + + +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp) +{ + char FullFrom[80]; + char FullTo[80]; + + strcpy(FullFrom, Msg->from); + + if ((_stricmp(Msg->from, "RMS:") == 0) || (_stricmp(Msg->from, "SMTP:") == 0) || + Temp->SendFullFrom || (_stricmp(Msg->emailfrom, "@winlink.org") == 0)) + strcat(FullFrom, Msg->emailfrom); + + if (_stricmp(Msg->to, "RMS") == 0) + { + sprintf(FullTo, "RMS:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + else + + if (Msg->to[0] == 0 && Msg->via[0] != 0) + { + sprintf(FullTo, "smtp:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + + else + if (Msg->via[0] != 0) + { + char Via[80]; + strcpy(Via, Msg->via); + strlop(Via, '.'); // Only show first part of via + nodeprintf(conn, "%-6d %s %c%c %5d %-7s@%-6s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, Via, FullFrom, Msg->title); + } + else + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, FullFrom, Msg->title); + + // if paging, stop two before page lengh. This lets us send the continue prompt, save status + // and exit without triggering the system paging code. We can then read a message then resume listing + + if (Temp->ListActive && conn->Paging) + { + Temp->LinesSent++; + + if ((Temp->LinesSent + 1) >= conn->PageLen) + { + nodeprintf(conn, "bort, , = Continue..>"); + Temp->LastListedInPagedMode = Msg->number; + Temp->ListSuspended = TRUE; + return TRUE; + } + } + + return FALSE; +} + +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context) +{ + struct TempUserInfo * Temp = user->Temp; + struct MsgInfo * Msg; + + // Allow compound selection, eg LTN or LFP + + // types P N T + // Options LL LR L< L> L@ LM LC (L* used internally for just L, ie List New + // Status N Y H F K D + + // Allowing options in any order complicates paging. May be best to parse options once and restore if paging. + + Temp->ListActive = TRUE; + Temp->LinesSent = 0; + + if (Resuming) + { + // Entered after a paging pause. Selection fields are already set up + + // We have reentered list command after a pause. The next message to list is in Temp->LastListedInPagedMode + +// Start = Temp->LastListedInPagedMode; + Temp->ListSuspended = FALSE; + } + else + { + Temp->ListRangeEnd = LatestMsg; + Temp->ListRangeStart = 1; + Temp->LLCount = 0; + Temp->SendFullFrom = 0; + Temp->ListType = 0; + Temp->ListStatus = 0; + Temp->ListSelector = 0; + Temp->UpdateLatest = 0; + Temp->LastListParams[0] = 0; + Temp->IncludeKilled = 1; // SYSOP include Killed except LM + + //Analyse L params. + + _strupr(Cmd); + + if (strcmp(Cmd, "LC") == 0) // List Bull Categories + { + ListCategories(conn); + return; + } + + // if command is just L or LR start from last listed + + if (Arg1 == NULL) + { + if (strcmp(Cmd, "L") == 0 || strcmp(Cmd, "LR") == 0) + { + if (LatestMsg == conn->lastmsg) + { + BBSputs(conn, "No New Messages\r"); + return; + } + + Temp->UpdateLatest = 1; + Temp->ListRangeStart = conn->lastmsg; + } + } + + if (strchr(Cmd, 'V')) // Verbose + Temp->SendFullFrom = 'V'; + + if (strchr(Cmd, 'R')) + Temp->ListDirn = 'R'; + else + Temp->ListDirn = '*'; // Default newest first + + Cmd++; // skip L + + if (strchr(Cmd, 'T')) + Temp->ListType = 'T'; + else if (strchr(Cmd, 'P')) + Temp->ListType = 'P'; + else if (strchr(Cmd, 'B')) + Temp->ListType = 'B'; + + if (strchr(Cmd, 'N')) + Temp->ListStatus = 'N'; + else if (strchr(Cmd, 'Y')) + Temp->ListStatus = 'Y'; + else if (strchr(Cmd, 'F')) + Temp->ListStatus = 'F'; + else if (strchr(Cmd, '$')) + Temp->ListStatus = '$'; + else if (strchr(Cmd, 'H')) + Temp->ListStatus = 'H'; + else if (strchr(Cmd, 'K')) + Temp->ListStatus = 'K'; + else if (strchr(Cmd, 'D')) + Temp->ListStatus = 'D'; + + // H or K only by Sysop + + switch (Temp->ListStatus) + { + case 'K': + case 'H': // List Status + + if (conn->sysop) + break; + + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + return; + } + + if (strchr(Cmd, '<')) + Temp->ListSelector = '<'; + else if (strchr(Cmd, '>')) + Temp->ListSelector = '>'; + else if (strchr(Cmd, '@')) + Temp->ListSelector = '@'; + else if (strchr(Cmd, 'M')) + { + Temp->ListSelector = 'M'; + Temp->IncludeKilled = FALSE; + } + + // Param could be single number, number range or call + + if (Arg1) + { + if (strchr(Cmd, 'L')) // List Last + { + // Param is number + + if (Arg1) + Temp->LLCount = atoi(Arg1); + } + else + { + // Range nnn-nnn or single value or callsign + + char * Arg2, * Arg3, * Range; + char seps[] = " \t\r"; + UINT From=LatestMsg, To=0; + + Arg2 = strtok_s(NULL, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Temp->ListSelector && Temp->ListSelector != 'M') + { + // < > or @ - first param is callsign + + strcpy(Temp->LastListParams, Arg1); + + // Just possible number range + + Arg1 = Arg2; + Arg2 = Arg3; + Arg3 = strtok_s(NULL, seps, &Context); + } + + if (Arg1) + { + Range = strchr(Arg1, '-'); + + // A number could be a Numeric Bull Dest (eg 44) + // I think this can only resaonably be > + + if (isdigits(Arg1)) + To = From = atoi(Arg1); + + if (Arg2) + From = atoi(Arg2); + else + { + if (Range) + { + Arg3 = strlop(Arg1, '-'); + + To = atoi(Arg1); + + if (Arg3 && Arg3[0]) + From = atoi(Arg3); + else + From = LatestMsg; + } + } + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + Temp->ListRangeStart = To; + Temp->ListRangeEnd = From; + } + } + } + } + + // Run through all messages (either forwards or backwards) and list any that match all selection criteria + + while (1) + { + if (Temp->ListDirn == 'R') + Msg = GetMsgFromNumber(Temp->ListRangeStart); + else + Msg = GetMsgFromNumber(Temp->ListRangeEnd); + + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop, Temp->IncludeKilled)) // Check if user is allowed to list this message + { + // Check filters + + if (Temp->ListStatus && Temp->ListStatus != Msg->status) + goto skip; + + if (Temp->ListType && Temp->ListType != Msg->type) + goto skip; + + if (Temp->ListSelector == '<') + if (_stricmp(Msg->from, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '>') + if (_stricmp(Msg->to, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '@') + if (_memicmp(Msg->via, Temp->LastListParams, strlen(Temp->LastListParams)) != 0 && + (_stricmp(Temp->LastListParams, "SMTP:") != 0 || Msg->to[0] != 0)) + goto skip; + + if (Temp->ListSelector == 'M') + if (_stricmp(Msg->to, user->Call) != 0 && + (_stricmp(Msg->to, "SYSOP") != 0 || ((user->flags & F_SYSOP_IN_LM) == 0))) + + goto skip; + + if (ListMessage(Msg, conn, Temp)) + { + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + return; // Hit page limit + } + + if (Temp->LLCount) + { + Temp->LLCount--; + if (Temp->LLCount == 0) + return; // LL count reached + } +skip:; + } + + if (Temp->ListRangeStart == Temp->ListRangeEnd) + { + // if using L or LR (list new) update last listed field + + if (Temp->UpdateLatest) + conn->lastmsg = LatestMsg; + + return; + } + + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + if (Temp->ListRangeStart > 100000 || Temp->ListRangeEnd < 0) // Loop protection! + return; + + } + +/* + + switch (Cmd[0]) + { + + case '*': // Just L + case 'R': // LR = List Reverse + + if (Arg1) + { + // Range nnn-nnn or single value + + char * Arg2, * Arg3; + char * Context; + char seps[] = " -\t\r"; + UINT From=LatestMsg, To=0; + char * Range = strchr(Arg1, '-'); + + Arg2 = strtok_s(Arg1, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Arg2) + To = From = atoi(Arg2); + + if (Arg3) + From = atoi(Arg3); + else + if (Range) + From = LatestMsg; + + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + if (Cmd[1] == 'R') + { + if (Start) + To = Start + 1; + + ListMessagesInRangeForwards(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + else + { + if (Start) + From = Start - 1; + + ListMessagesInRange(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + } + else + + if (LatestMsg == conn->lastmsg) + BBSputs(conn, "No New Messages\r"); + else if (Cmd[1] == 'R') + ListMessagesInRangeForwards(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + else + ListMessagesInRange(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + + conn->lastmsg = LatestMsg; + + return; + + + case 'L': // List Last + + if (Arg1) + { + int i = atoi(Arg1); + int m = NumberofMessages; + + if (Resuming) + i = Temp->LLCount; + else + Temp->LLCount = i; + + for (; i>0 && m != 0; i--) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + i++; + continue; + } + + Temp->LLCount--; + + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + } + return; + + case 'M': // LM - List Mine + + if (ListMessagesTo(conn, user, user->Call, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + return; + + case '>': // L> - List to + + if (Arg1) + if (ListMessagesTo(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + + return; + + case '<': + + if (Arg1) + if (ListMessagesFrom(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case '@': + + if (Arg1) + if (ListMessagesAT(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case 'N': + case 'Y': + case 'F': + case '$': + case 'D': // Delivered NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (Temp->ListType) + { + if (MsgHddrPtr[m]->status == Cmd[1] && MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + else + { + if (MsgHddrPtr[m]->status == toupper(Cmd[1])) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + m--; + } + } + } + return; + + case 'K': + case 'H': // List Status + + if (conn->sysop) + { + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (MsgHddrPtr[i]->status == toupper(Cmd[1])) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, SendFullFrom)) + return; // Hit page limit + + } + } + + if (Msgs == 0) + BBSputs(conn, "No Messages found\r"); + } + else + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + + return; + + case 'C': + { + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; + Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; + } + } + + // Could be P B or T if specified without a status + + switch (Temp->ListType) + { + case 'P': + case 'B': + case 'T': // NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + + return; + } + } + +*/ + nodeprintf(conn, "*** Error: Invalid List option %c\r", Cmd[1]); + +} + +void ListCategories(ConnectionInfo * conn) +{ + // list bull categories + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; +Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; +} + +/* +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if ((_stricmp(MsgHddrPtr[i]->to, Call) == 0) || + ((conn->sysop) && _stricmp(Call, SYSOPCall) == 0 && + _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0 && (user->flags & F_SYSOP_IN_LM))) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} + +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_stricmp(MsgHddrPtr[i]->from, Call) == 0) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + return Msgs; // Hit page limit + + } + } + + return(Msgs); +} + +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom,int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_memicmp(MsgHddrPtr[i]->via, Call, strlen(Call)) == 0 || + (_stricmp(Call, "SMTP:") == 0 && MsgHddrPtr[i]->to[0] == 0)) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} +*/ +int GetUserMsg(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'K') + { + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m--; + + } while (m> 0); + + return 0; +} + + +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled) +{ + // Return TRUE if user is allowed to read message + + if (Msg->status == 'K' && IncludeKilled == 0) + return FALSE; + + if (SYSOP) + return TRUE; // Sysop can list or read anything + + if ((Msg->status != 'K') && (Msg->status != 'H')) + { + if (Msg->type == 'B' || Msg->type == 'T') return TRUE; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + } + } + + return FALSE; +} +/* +int GetUserMsgForwards(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->status != 'K') + { + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m++; + + } while (m <= NumberofMessages); + + return 0; + +} + + +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m >= End; m--) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + + } +} + + +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int End, int Start, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m <= End; m++) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + } +} +*/ + +void DoReadCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + + switch (toupper(Cmd[1])) + { + case 0: // Just R + + while (Arg1) + { + msgno = atoi(Arg1); + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + ReadMessage(conn, user, msgno); + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; + + case 'M': // Read Mine (Unread Messages) + + if (toupper(Cmd[2]) == 'R') + { + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + else + { + for (i = NumberofMessages; i > 0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + + return; + } + + nodeprintf(conn, "*** Error: Invalid Read option %c\r", Cmd[1]); + + return; +} + +int RemoveLF(char * Message, int len) +{ + // Remove lf chars and nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + if (*ptr1 == '\r') + if (*(ptr1+1) == '\n') + { + ptr1++; + len--; + } + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + + + +int RemoveNulls(char * Message, int len) +{ + // Remove nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + char * MsgBytes, * Save; + char FullTo[100]; + int Index = 0; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (!CheckUserMsg(Msg, user->Call, conn->sysop, TRUE)) + { + nodeprintf(conn, "Message %d not for you\r", msgno); + return; + } + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + nodeprintf(conn, "From: %s%s\rTo: %s\rType/Status: %c%c\rDate/Time: %s\rBid: %s\rTitle: %s\r\r", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + MsgBytes = Save = ReadMessageFile(msgno); + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + if (MsgBytes) + { + int Length = Msg->length; + + if (Msg->B2Flags & B2Msg) + { + char * ptr; + + // if message has attachments, display them if plain text + + if (Msg->B2Flags & Attachments) + { + char * FileName[100]; + int FileLen[100]; + int Files = 0; + int BodyLen, NewLen; + int i; + char *ptr2; + char Msg[512]; + int Len; + + ptr = MsgBytes; + + Len = sprintf(Msg, "Message has Attachments\r\r"); + QueueMsg(conn, Msg, Len); + + while(*ptr != 13) + { + ptr2 = strchr(ptr, 10); // Find CR + + if (memcmp(ptr, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr[6]); + } + + if (memcmp(ptr, "File: ", 6) == 0) + { + char * ptr1 = strchr(&ptr[6], ' '); // Find Space + + FileLen[Files] = atoi(&ptr[6]); + + FileName[Files++] = &ptr1[1]; + *(ptr2 - 1) = 0; + } + + ptr = ptr2; + ptr++; + } + + ptr += 2; // Over Blank Line and Separator + + NewLen = RemoveLF(ptr, BodyLen); + + QueueMsg(conn, ptr, NewLen); // Display Body + + ptr += BodyLen + 2; // to first file + + for (i = 0; i < Files; i++) + { + char Msg[512]; + int Len, n; + char * p = ptr; + char c; + + // Check if message is probably binary + + int BinCount = 0; + + NewLen = RemoveLF(ptr, FileLen[i]); // Removes LF agter CR but not on its own + + for (n = 0; n < NewLen; n++) + { + c = *p; + + if (c == 10) + *p = 13; + + if (c==0 || (c & 128)) + BinCount++; + + p++; + + } + + if (BinCount > NewLen/10) + { + // File is probably Binary + + Len = sprintf(Msg, "\rAttachment %s is a binary file\r", FileName[i]); + QueueMsg(conn, Msg, Len); + } + else + { + Len = sprintf(Msg, "\rAttachment %s\r\r", FileName[i]); + QueueMsg(conn, Msg, Len); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += NewLen; + + QueueMsg(conn, ptr, NewLen); + } + + ptr += FileLen[i]; + ptr +=2; // Over separator + } + goto sendEOM; + } + + // Remove B2 Headers (up to the File: Line) + + ptr = strstr(MsgBytes, "Body:"); + + if (ptr) + { + MsgBytes = ptr; + Length = (int)strlen(ptr); + } + } + + // Remove lf chars + + Length = RemoveLF(MsgBytes, Length); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += Length; + + QueueMsg(conn, MsgBytes, Length); + +sendEOM: + + free(Save); + + nodeprintf(conn, "\r\r[End of Message #%d from %s%s]\r", msgno, Msg->from, Msg->emailfrom); + + if ((_stricmp(Msg->to, user->Call) == 0) || ((conn->sysop) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + } + } + } + } + else + { + nodeprintf(conn, "File for Message %d not found\r", msgno); + } +} + struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + m = GetUserMsg(m, Call, sysop); + + if (m == 0) + return NULL; + + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + m--; + + } while (m> 0); + + return NULL; + +} + + +char * ReadInfoFile(char * File) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + char * ptr1 = 0, * ptr2; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BaseDir, File); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize] = 0; + + ptr1 = MsgBytes; + + // Replace LF or CRLF with CR + + // First remove cr from crlf + + while(ptr2 = strstr(ptr1, "\r\n")) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + } + + // Now replace lf with cr + + ptr1 = MsgBytes; + + while (*ptr1) + { + if (*ptr1 == '\n') + *(ptr1) = '\r'; + + ptr1++; + } + + return MsgBytes; +} + +char * FormatDateAndTime(time_t Datim, BOOL DateOnly) +{ + struct tm *tm; + static char Date[]="xx-xxx hh:mmZ"; + + tm = gmtime(&Datim); + + if (tm) + sprintf_s(Date, sizeof(Date), "%02d-%3s %02d:%02dZ", + tm->tm_mday, month[tm->tm_mon], tm->tm_hour, tm->tm_min); + + if (DateOnly) + { + Date[6]=0; + return Date; + } + + return Date; +} + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); + + +BOOL DoSendCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * OldMsg; + char OldTitle[62]; + char NewTitle[62]; + char To[100]= ""; + int msgno; + + if (Cmd[1] == 0) Cmd[1] ='P'; // Just S means SP + + switch (toupper(Cmd[1])) + { + case 'B': + + if (RefuseBulls) + { + nodeprintf(conn, "*** Error: This system doesn't allow sending Bulls\r"); + return FALSE; + } + + if (user->flags & F_NOBULLS) + { + nodeprintf(conn, "*** Error: You are not allowed to send Bulls\r"); + return FALSE; + } + + + case 'P': + case 'T': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + return CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL); + + case 'R': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + Arg1=&OldMsg->from[0]; + + strcpy(To, Arg1); + + if (_stricmp(Arg1, "SMTP:") == 0 || _stricmp(Arg1, "RMS:") == 0 || OldMsg->emailfrom) + { + // SMTP message. Need to get the real sender from the message + + sprintf(To, "%s%s", Arg1, OldMsg->emailfrom); + } + + if (!DecodeSendParams(conn, "", &From, To, &ATBBS, &BID)) + return FALSE; + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 57) OldTitle[57] = 0; + + strcpy(NewTitle, "Re:"); + strcat(NewTitle, OldTitle); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + + return TRUE; + + case 'C': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 56) OldTitle[56] = 0; + + strcpy(NewTitle, "Fwd:"); + strcat(NewTitle, OldTitle); + + conn->CopyBuffer = ReadMessageFile(msgno); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + } + + + nodeprintf(conn, "*** Error: Invalid Send option %c\r", Cmd[1]); + + return FALSE; +} + +char * CheckToAddress(CIRCUIT * conn, char * Addr) +{ + // Check one element of Multiple Address + + if (conn == NULL || !(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(Addr, "SYSOP") == 0) + { + return _strdup(Addr); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(Addr, BBSName) == 0) + return _strdup(SYSOPCall); + + + if (strchr(Addr, '@') == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + char * NewAddr = malloc(250); + if (conn) + nodeprintf(conn, "Address %s - @%s added from HomeBBS\r", Addr, ToUser->HomeBBS); + sprintf(NewAddr, "%s@%s", Addr, ToUser->HomeBBS); + return NewAddr; + } + } + else + { + WPRecP WP = LookupWP(Addr); + + if (WP) + { + char * NewAddr = malloc(250); + + if (conn) + nodeprintf(conn, "Address %s - @%s added from WP\r", Addr, WP->first_homebbs); + sprintf(NewAddr, "%s@%s", Addr, WP->first_homebbs); + return NewAddr; + } + } + } + } + + // Check SMTP and RMS Addresses + + if ((_memicmp(Addr, "rms:", 4) == 0) || (_memicmp(Addr, "rms/", 4) == 0)) + { + Addr[3] = ':'; // Replace RMS/ with RMS: + + if (conn && !FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + } + else if ((_memicmp(Addr, "smtp:", 5) == 0) || (_memicmp(Addr, "smtp/", 5) == 0)) + { + Addr[4] = ':'; // Replace smpt/ with smtp: + + if (ISP_Gateway_Enabled) + { + if (conn && (conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + } + else + { + if (conn) + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + + return _strdup(Addr); +} + + +char Winlink[] = "WINLINK.ORG"; + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char *To, char ** ATBBS, char ** BID) +{ + char * ptr; + char seps[] = " \t\r"; + WPRecP WP; + char * ToCopy = _strdup(To); + int Len; + + conn->ToCount = 0; + + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + // Having trailing ; will mess up parsing multiple addresses, so remove. + + while (To[strlen(To) - 1] == ';') + To[strlen(To) - 1] = 0; + + if (strchr(Context, ';') || strchr(To, ';')) + { + // Multiple Addresses - put address list back together + + char * p; + + To[strlen(To)] = ' '; + Context = To; + + while (p = strchr(Context, ';')) + { + // Multiple Addressees + + To = strtok_s(NULL, ";", &Context); + Len = (int)strlen(To); + conn->To = realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + + To = strtok_s(NULL, seps, &Context); + + Len = (int)strlen(To); + conn->To=realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + else + { + // Single Call + + // accept CALL!CALL for source routed message + + if (strchr(To, '@') == 0 && strchr(To, '!')) // Bang route without @ + { + char * bang = strchr(To, '!'); + + memmove(bang + 1, bang, strlen(bang)); // Move !call down one + + *ATBBS = strlop(To, '!');; + } + + // Accept call@call (without spaces) - but check for smtp addresses + + if (_memicmp(To, "smtp:", 5) != 0 && _memicmp(To, "rms:", 4) != 0 && _memicmp(To, "rms/", 4) != 0) + { + ptr = strchr(To, '@'); + + if (ptr) + { + // If looks like a valid email address, treat as such + + int tolen; + *ATBBS = strlop(To, '@'); + + strlop(To, '-'); // Cant have SSID on BBS Name + + tolen = (int)strlen(To); + + if (tolen > 6 || !CheckifPacket(*ATBBS)) + { + // Probably Email address. Add smtp: or rms: + + if (FindRMS() || strchr(*ATBBS, '!')) // have RMS or source route + sprintf(To, "rms:%s", ToCopy); + else if (ISP_Gateway_Enabled) + sprintf(To, "smtp:%s", ToCopy); + else if (isAMPRMsg(ToCopy)) + sprintf(To, "rms:%s", ToCopy); + + } + } + } + } + + free(ToCopy); + + // Look for Optional fields; + + ptr = strtok_s(NULL, seps, &Context); + + while (ptr) + { + if (strcmp(ptr, "@") == 0) + { + *ATBBS = _strupr(strtok_s(NULL, seps, &Context)); + } + else if(strcmp(ptr, "<") == 0) + { + *From = strtok_s(NULL, seps, &Context); + } + else if (ptr[0] == '$') + *BID = &ptr[1]; + else + { + nodeprintf(conn, "*** Error: Invalid Format\r"); + return FALSE; + } + ptr = strtok_s(NULL, seps, &Context); + } + + // Only allow < from a BBS + + if (*From) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "*** < can only be used by a BBS\r"); + return FALSE; + } + } + + if (!*From) + *From = conn->UserPointer->Call; + + if (!(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(To, "SYSOP") == 0) + { + conn->LocalMsg = TRUE; + return TRUE; + } + + if (!*ATBBS && conn->ToCount == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(To); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + *ATBBS = Winlink; + nodeprintf(conn, "Redirecting to winlink.org\r", *ATBBS); + } + else if (ToUser->HomeBBS[0]) + { + *ATBBS = ToUser->HomeBBS; + nodeprintf(conn, "Address @%s added from HomeBBS\r", *ATBBS); + } + else + { + conn->LocalMsg = TRUE; + } + } + else + { + conn->LocalMsg = FALSE; + WP = LookupWP(To); + + if (WP) + { + *ATBBS = WP->first_homebbs; + nodeprintf(conn, "Address @%s added from WP\r", *ATBBS); + } + } + } + } + return TRUE; +} + +BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title) +{ + struct MsgInfo * Msg, * TestMsg; + char * via = NULL; + char * FromHA; + + // Create a temp msg header entry + + if (conn->ToCount) + { + } + else + { + if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType)) + { + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - REJECTED\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error - Message Filters prevent sending this message\r"); + + return FALSE; + } + } + + Msg = malloc(sizeof (struct MsgInfo)); + + if (Msg == 0) + { + CriticalErrorHandler("malloc failed for new message header"); + return FALSE; + } + + memset(Msg, 0, sizeof (struct MsgInfo)); + + conn->TempMsg = Msg; + + Msg->type = MsgType; + + if (conn->UserPointer->flags & F_HOLDMAIL) + Msg->status = 'H'; + else + Msg->status = 'N'; + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (BID) + { + BIDRec * TempBID; + + // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message + // If we do, reject it. If not, accept it again. (do we need some loop protection ???) + + TempBID = LookupBID(BID); + + if (TempBID) + { + if (MsgType == 'B') + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + + TestMsg = GetMsgFromNumber(TempBID->u.msgno); + + // if the same TO we will assume the same message + + if (TestMsg && strcmp(TestMsg->to, ToCall) == 0) + { + // We have this message. If we have already forwarded it, we should accept it again + + if ((TestMsg->status == 'N') || (TestMsg->status == 'Y')|| (TestMsg->status == 'H')) + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + } + } + + if (strlen(BID) > 12) BID[12] = 0; + strcpy(Msg->bid, BID); + + // Save BID in temp list in case we are offered it again before completion + + TempBID = AllocateTempBIDRecord(); + strcpy(TempBID->BID, BID); + TempBID->u.conn = conn; + + } + + if (conn->ToCount) + { + } + else + { + if (_memicmp(ToCall, "rms:", 4) == 0) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + + via=strlop(ToCall, ':'); + _strupr(ToCall); + } + else if (_memicmp(ToCall, "rms/", 4) == 0) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + + via=strlop(ToCall, '/'); + _strupr(ToCall); + } + else if (_memicmp(ToCall, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + if ((conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + via=strlop(ToCall, ':'); + ToCall[0] = 0; + } + else + { + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + else + { + _strupr(ToCall); + if (ATBBS) + via=_strupr(ATBBS); + } + + strlop(ToCall, '-'); // Remove any (illegal) ssid + if (strlen(ToCall) > 6) ToCall[6] = 0; + + strcpy(Msg->to, ToCall); + + if (SendBBStoSYSOPCall) + if (_stricmp(ToCall, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (via) + { + if (strlen(via) > 40) via[40] = 0; + + strcpy(Msg->via, via); + } + + } // End of Multiple Dests + + // Look for HA in From (even if we shouldn't be getting it!) + + FromHA = strlop(From, '@'); + + + strlop(From, '-'); // Remove any (illegal) ssid + if (strlen(From) > 6) From[6] = 0; + strcpy(Msg->from, From); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + if (Title) // Only used by SR and SC + { + strcpy(Msg->title, Title); + conn->Flags |= GETTINGMESSAGE; + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + return TRUE; + } + + if (conn->BBSFlags & FLARQMODE) + return TRUE; + + if (!(conn->BBSFlags & FBBCompressed)) + conn->Flags |= GETTINGTITLE; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "Enter Title (only):\r"); + else + if (!(conn->BBSFlags & FBBForwarding)) + nodeprintf(conn, "OK\r"); + + return TRUE; +} + +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + + conn->Flags &= ~GETTINGTITLE; + + if (msglen == 1) + { + nodeprintf(conn, "*** Message Cancelled\r"); + SendPrompt(conn, user); + return; + } + + if (msglen > 60) msglen = 60; + + Buffer[msglen-1] = 0; + + strcpy(conn->TempMsg->title, Buffer); + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to create Message Buffer\r"); + return; + } + + conn->Flags |= GETTINGMESSAGE; + + if (!conn->BBSFlags & BBS) + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + +} + +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + char * ptr2 = NULL; + + if (((msglen < 3) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3) == 0))) + { + int Index = 0; + + if (conn->TempMsg->type == 'P') + Index = PMSG; + else if (conn->TempMsg->type == 'B') + Index = BMSG; + else if (conn->TempMsg->type == 'T') + Index = TMSG; + + conn->Flags &= ~GETTINGMESSAGE; + + user->Total.MsgsReceived[Index]++; + user->Total.BytesForwardedIn[Index] += conn->TempMsg->length; + + if (conn->ToCount) + { + // Multiple recipients + + struct MsgInfo * Msg = conn->TempMsg; + int i; + struct MsgInfo * SaveMsg = Msg; + char * SaveBody = conn->MailBuffer; + int SaveMsgLen = Msg->length; + BOOL SentToRMS = FALSE; + int ToLen = 0; + char * ToString = zalloc(conn->ToCount * 100); + + // If no BID provided, allocate one + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg + 1, BBSName); + + for (i = 0; i < conn->ToCount; i++) + { + char * Addr = conn->To[i]; + char * Via; + + if (_memicmp (Addr, "SMTP:", 5) == 0) + { + // For Email + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + Msg->to[0] = 0; + strcpy(Msg->via, &Addr[5]); + + CreateMessageFromBuffer(conn); + continue; + } + + if (_memicmp (Addr, "RMS:", 4) == 0) + { + // Add to B2 Message for RMS + + Addr+=4; + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + goto PktMsg; + } + else + { + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + continue; + } + } + + ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, Addr, Via); + continue; + } + + _strupr(Addr); + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + } + else + { + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + + // Add to B2 Message for RMS + + continue; + } + } + + PktMsg: + + conn->LocalMsg = FALSE; + + // Normal BBS Message + + if (_stricmp(Addr, "SYSOP") == 0) + conn->LocalMsg = TRUE; + else + { + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + conn->LocalMsg = TRUE; + } + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + strcpy(Msg->to, Addr); + + if (Via) + { + Msg->bid[0] = 0; // if we are forwarding it, we must change BID to be safe + strcpy(Msg->via, Via); + } + + CreateMessageFromBuffer(conn); + } + + if (ToLen) + { + char * B2Hddr = zalloc(ToLen + 1000); + int B2HddrLen; + char DateString[80]; + struct tm * tm; + time_t Date = time(NULL); + char Type[16] = "Private"; + + // Get Type + + if (conn->TempMsg->type == 'B') + strcpy(Type, "Bulletin"); + else if (conn->TempMsg->type == 'T') + strcpy(Type, "Traffic"); + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000 + ToLen); + + Msg->B2Flags = B2Msg; + + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n", + SaveMsg->bid, DateString, Type, + SaveMsg->from, ToString, SaveMsg->title, BBSName, SaveMsgLen); + + memcpy(conn->MailBuffer, B2Hddr, B2HddrLen); + memcpy(&conn->MailBuffer[B2HddrLen], SaveBody, SaveMsgLen); + + Msg->length += B2HddrLen; + + strcpy(Msg->to, "RMS"); + + CreateMessageFromBuffer(conn); + + free(B2Hddr); + } + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + if (!(conn->BBSFlags & BBS)) + SendPrompt(conn, conn->UserPointer); + else + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->BBSFlags & OUTWARDCONNECT) + BBSputs(conn, "F>\r"); // if Outward connect must be reverse forward + else + BBSputs(conn, ">\r"); + } + + /* + // From a client - Create one copy with all RMS recipients, and another for each packet recipient + + // Merge all RMS To: lines + + ToLen = 0; + ToString[0] = 0; + + for (i = 0; i < Recipients; i++) + { + if (LocalMsg[i]) + continue; // For a local RMS user + + if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + { + ToLen += strlen(HddrTo[i]); + strcat(ToString, HddrTo[i]); + } + } + + if (ToLen) + { + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], ToString, ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, "RMS"); + strcpy(Msg->via, "winlink.org"); + + // Must Change the BID + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + + } + + free(ToString); + + for (i = 0; i < Recipients; i++) + { + // Only Process Non - RMS Dests or local RMS Users + + if (LocalMsg[i] == 0) + if (_stricmp (Via[i], "WINLINK.ORG") == 0 || + _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ToLen = strlen(HddrTo[i]); + + if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + } // End not from RMS + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + SetupNextFBBMessage(conn); + return; + + } My__except_Routine("Process Multiple Destinations"); + + BBSputs(conn, "*** Program Error Processing Multiple Destinations\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; +*/ + + conn->ToCount = 0; + + return; + } + + + CreateMessageFromBuffer(conn); + return; + + } + + Buffer[msglen++] = 0x0a; + + if ((conn->TempMsg->length + msglen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, msglen); + + conn->TempMsg->length += msglen; +} + +VOID CreateMessageFromBuffer(CIRCUIT * conn) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * ptr1, * ptr2 = NULL; + char * ptr3, * ptr4; + int FWDCount = 0; + char OldMess[] = "\r\n\r\nOriginal Message:\r\n\r\n"; + time_t Age; + int OurCount; + char * HoldReason = "User has Hold Messages flag set"; + struct UserInfo * user; + + +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; +#endif + + // If doing SC, Append Old Message + + if (conn->CopyBuffer) + { + if ((conn->TempMsg->length + (int) strlen(conn->CopyBuffer) + 80 )> conn->MailBufferSize) + { + conn->MailBufferSize += (int)strlen(conn->CopyBuffer) + 80; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], OldMess, strlen(OldMess)); + + conn->TempMsg->length += (int)strlen(OldMess); + + memcpy(&conn->MailBuffer[conn->TempMsg->length], conn->CopyBuffer, strlen(conn->CopyBuffer)); + + conn->TempMsg->length += (int)strlen(conn->CopyBuffer); + + free(conn->CopyBuffer); + conn->CopyBuffer = NULL; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + memcpy(Msg, conn->TempMsg, sizeof(struct MsgInfo)); + + free(conn->TempMsg); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + if (Msg->status == 0) + Msg->status = 'N'; + + // Create BID if non supplied + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + // if message body had R: lines, get date created from last (not very accurate, but best we can do) + + // Also check if we have had message before to detect loops + + ptr1 = conn->MailBuffer; + OurCount = 0; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr1 = strstr(ptr1, "\r\n\r\n"); + if (ptr1) + ptr1 += 4; + else + ptr1 = conn->MailBuffer; + } + +nextline: + + if (memcmp(ptr1, "R:", 2) == 0) + { + // Is if ours? + + // BPQ RLINE Format R:090920/1041Z 6542@N4JOA.#WPBFL.FL.USA.NOAM BPQ1.0.2 + + ptr3 = strchr(ptr1, '@'); + ptr4 = strchr(ptr1, '.'); + + if (ptr3 && ptr4 && (ptr4 > ptr3)) + { + if (memcmp(ptr3+1, BBSName, ptr4-ptr3-1) == 0) + OurCount++; + } + + GetWPBBSInfo(ptr1); // Create WP /I record from R: Line + + // see if another + + ptr2 = ptr1; // save + ptr1 = strchr(ptr1, '\r'); + if (ptr1 == 0) + { + Debugprintf("Corrupt Message %s from %s - truncated within R: line", Msg->bid, Msg->from); + return; + } + ptr1++; + if (*ptr1 == '\n') ptr1++; + + goto nextline; + } + + // ptr2 points to last R: line (if any) + + if (ptr2) + { + struct tm rtime; + time_t result; + + memset(&rtime, 0, sizeof(struct tm)); + + if (ptr2[10] == '/') + { + // Dodgy 4 char year + + sscanf(&ptr2[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (ptr2[8] == '/') + { + sscanf(&ptr2[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + // result = _mkgmtime(&rtime); + + if ((result = mktime(&rtime)) != (time_t)-1 ) + { + result -= (time_t)_MYTIMEZONE; + + Msg->datecreated = result; + Age = (time(NULL) - result)/86400; + + if ( Age < -7) + { + Msg->status = 'H'; + HoldReason = "Suspect Date Sent"; + } + else if (Age > BidLifetime || Age > MaxAge) + { + Msg->status = 'H'; + HoldReason = "Message too old"; + + } + else + GetWPInfoFromRLine(Msg->from, ptr2, result); + } + else + { + // Can't decode R: Datestamp + + Msg->status = 'H'; + HoldReason = "Corrupt R: Line - can't determine age"; + } + + if (OurCount > 1) + { + // Message is looping + + Msg->status = 'H'; + HoldReason = "Message may be looping"; + + } + } + + if (strcmp(Msg->to, "WP") == 0) + { + // If Reject WP Bulls is set, Kill message here. + // It should only get here if B2 - otherwise it should be + // rejected earlier + + if (Msg->type == 'B' && FilterWPBulls) + Msg->status = 'K'; + + } + + conn->MailBuffer[Msg->length] = 0; + + if (CheckBadWords(Msg->title) || CheckBadWords(conn->MailBuffer)) + { + Msg->status = 'H'; + HoldReason = "Bad word in title or body"; + } + + if (CheckHoldFilters(Msg->from, Msg->to, Msg->via, Msg->bid)) + { + Msg->status = 'H'; + HoldReason = "Matched Hold Filters"; + } + + if (CheckValidCall(Msg->from) == 0) + { + Msg->status = 'H'; + HoldReason = "Probable Invalid From Call"; + } + + // Process any WP Messages + + if (strcmp(Msg->to, "WP") == 0) + { + if (Msg->status == 'N') + { + ProcessWPMsg(conn->MailBuffer, Msg->length, ptr2); + + if (Msg->type == 'P') // Kill any processed private WP messages. + { + char VIA[80]; + + strcpy(VIA, Msg->via); + strlop(VIA, '.'); + + if (strcmp(VIA, BBSName) == 0) + Msg->status = 'K'; + } + } + } + + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (Msg->length > MaxTXSize) + { + Msg->status = 'H'; + HoldReason = "Message too long"; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "*** Warning Message length exceeds sysop-defined maximum of %d - Message will be held\r", MaxTXSize); + } + + // Check for message to internal server + + if (Msg->via[0] == 0 + || _stricmp(Msg->via, BBSName) == 0 // our BBS a + || _stricmp(Msg->via, AMPRDomain) == 0) // our AMPR Address + { + if (CheckforMessagetoServer(Msg)) + { + // Flag as killed and send prompt + + FlagAsKilled(Msg, TRUE); + + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message %d to Server Processed and Killed.\r", Msg->number); + SendPrompt(conn, conn->UserPointer); + } + return; // no need to process further + } + } + + if (Msg->to[0]) + FWDCount = MatchMessagetoBBSList(Msg, conn); + else + { + // If addressed @winlink.org, and to a local user, Keep here. + + char * Call; + char * AT; + + // smtp or rms - don't warn no route + + FWDCount = 1; + + Call = _strupr(_strdup(Msg->via)); + AT = strlop(Call, '@'); + + if (AT && _stricmp(AT, "WINLINK.ORG") == 0) + { + struct UserInfo * user = LookupCall(Call); + + if (user) + { + if (user->flags & F_POLLRMS) + { + Logprintf(LOG_BBS, conn, '?', "SMTP Message @ winlink.org, but local RMS user - leave here"); + strcpy(Msg->to, Call); + strcpy(Msg->via, AT); + if (user->flags & F_BBS) // User is a BBS, so set FWD bit so he can get it + set_fwd_bit(Msg->fbbs, user->BBSNumber); + + } + } + } + free(Call); + } + + // Warn SYSOP if P or T forwarded in, and has nowhere to go + + if ((conn->BBSFlags & BBS) && Msg->type != 'B' && FWDCount == 0 && WarnNoRoute && + strcmp(Msg->to, "SYSOP") && strcmp(Msg->to, "WP")) + { + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + SendWarningToSYSOP(Msg); + } + else + { + // No via - is it for a local user? + + if (LookupCall(Msg->to) == 0) + SendWarningToSYSOP(Msg); + } + } + + if ((conn->BBSFlags & SYNCMODE) == 0) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message: %d Bid: %s Size: %d\r", Msg->number, Msg->bid, Msg->length); + + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + + if (FWDCount == 0 && Msg->to[0] != 0) // unless smtp msg + nodeprintf(conn, "@BBS specified, but no forwarding info is available - msg may not be delivered\r"); + } + else + { + if (FWDCount == 0 && conn->LocalMsg == 0 && Msg->type != 'B') + // Not Local and no forward route + nodeprintf(conn, "Message is not for a local user, and no forwarding info is available - msg may not be delivered\r"); + } + if (conn->ToCount == 0) + SendPrompt(conn, conn->UserPointer); + } + else + { + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->ToCount == 0) + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + } + } + + if(Msg->to[0] == 0) + SMTPMsgCreated=TRUE; + + if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + if (Msg->status == 'H') + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, HoldReason); + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + // If Event Notifications enabled report a new message event + + if (reportNewMesageEvents) + { + char msg[200]; + + //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + struct tm *tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(msg, sizeof(msg),"%-6d %c %6d %-13s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->length, Msg->to, + Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); + +#ifdef WIN32 + if (pRunEventProgram) + pRunEventProgram("MailNewMsg.exe", msg); +#else + { + char prog[256]; + sprintf(prog, "%s/%s", BPQDirectory, "MailNewMsg"); + RunEventProgram(prog, msg); + } +#endif + } + + + if (EnableUI) +#ifdef LINBPQ + SendMsgUI(Msg); +#else + __try + { + SendMsgUI(Msg); + } + My__except_Routine("SendMsgUI"); +#endif + + user = LookupCall(Msg->to); + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + return; +} + +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg) +{ + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + char Mess[255]; + int len; + BOOL AutoImport = FALSE; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + // If title is "Batched messages for AutoImport from BBS xxxxxx and first line is S? and it is + // for this BBS, Import file and set message as Killed. May need to strip B2 Header and R: lines + + if (strcmp(Msg->to, BBSName) == 0 && strstr(Msg->title, "Batched messages for AutoImport from BBS ")) + { + UCHAR * ptr = conn->MailBuffer; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr = strstr(ptr, "\r\n\r\n"); + if (ptr) + ptr += 4; + else + ptr = conn->MailBuffer; + } + + if (memcmp(ptr, "R:", 2) == 0) + { + ptr = strstr(ptr, "\r\n\r\n"); //And remove R: Lines + if (ptr) + ptr += 4; + } + + if (*(ptr) == 'S' && ptr[2] == ' ') + { + int HeaderLen = (int)(ptr - conn->MailBuffer); + Msg->length -= HeaderLen; + memmove(conn->MailBuffer, ptr, Msg->length); + Msg->status = 'K'; + AutoImport = TRUE; + } + } + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(conn->MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + if (AutoImport) + ImportMessages(NULL, MsgFile, TRUE); + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (WriteLen != Msg->length) + { + len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); + QueueMsg(conn, Mess, len); + Debugprintf(Mess); + } + return; +} + + + + +VOID SendUnbuffered(int stream, char * msg, int len) +{ +#ifndef LINBPQ + if (stream < 0) + WritetoConsoleWindow(stream, msg, len); + else +#endif + SendMsg(stream, msg, len); +} + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen); + +BOOL FindMessagestoForward (CIRCUIT * conn) +{ + struct UserInfo * user = conn->UserPointer; + +#ifndef LINBPQ + + struct _EXCEPTION_POINTERS exinfo; + + __try { +#endif + + // This is a hack so Winpack or WLE users can use forwarding + // protocols to get their messages without being defined as a BBS + + // !!IMPORTANT Getting this wrong can see message repeatedly proposed !! + // !! Anything sent using this must be killed if sent or rejected. + + // I'm not sure about this. I think I only need the PacLinkCalls + // if from RMS Express, and it always sends an FW; line + // Ah, no. What about WinPack ?? + // If from RMS Express must have Temp_B2 or BBS set or SID will + // be rejected. So maybe just Temp_B2 && BBS = 0?? + // No, someone may have Temp_B2 set and not be using WLE ?? So what ?? + + if ((user->flags & F_Temp_B2_BBS) && ((user->flags & F_BBS) == 0) || conn->RMSExpress || conn->PAT) + { + if (conn->PacLinkCalls == NULL) + { + // create a list with just the user call + + char * ptr1; + + conn->PacLinkCalls = zalloc(30); + + ptr1 = (char *)conn->PacLinkCalls; + ptr1 += 16; // Must be room for a null pointer on end (64 bit bug) + strcpy(ptr1, user->Call); + + conn->PacLinkCalls[0] = ptr1; + } + } + + if (conn->SendT && FindMessagestoForwardLoop(conn, 'T', conn->MaxTLen)) + { + conn->LastForwardType = 'T'; + return TRUE; + } + + if (conn->LastForwardType == 'T') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendP && FindMessagestoForwardLoop(conn, 'P', conn->MaxPLen)) + { + conn->LastForwardType = 'P'; + return TRUE; + } + + if (conn->LastForwardType == 'P') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendB && FindMessagestoForwardLoop(conn, 'B', conn->MaxBLen)) + { + conn->LastForwardType = 'B'; + return TRUE; + } + + conn->LastForwardType = 0; + return FALSE; +#ifndef LINBPQ + } My__except_Routine("FindMessagestoForward"); +#endif + return FALSE; + +} + + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen) +{ + // See if any messages are queued for this BBS + + int m; + struct MsgInfo * Msg; + struct UserInfo * user = conn->UserPointer; + struct FBBHeaderLine * FBBHeader; + BOOL Found = FALSE; + char RLine[100]; + int TotalSize = 0; + time_t NOW = time(NULL); + +// Debugprintf("FMTF entered Call %s Type %c Maxlen %d NextMsg = %d BBSNo = %d", +// conn->Callsign, Type, MaxLen, conn->NextMessagetoForward, user->BBSNumber); + + if (conn->PacLinkCalls || (conn->UserPointer->flags & F_NTSMPS)) // Looking for all messages, so reset + conn->NextMessagetoForward = 1; + + conn->FBBIndex = 0; + + for (m = conn->NextMessagetoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If an NTS MPS, see if anything matches + + if (Type == 'T' && (conn->UserPointer->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + int depth; + + if (Msg->type == 'T' && Msg->status == 'N' && Msg->length <= MaxLen && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + } + } + + // If forwarding to Paclink or RMS Express, look for any message matching the + // requested call list with status 'N' (maybe should also be 'P' ??) + + if (conn->PacLinkCalls) + { + int index = 1; + + char * Call = conn->PacLinkCalls[0]; + + while (Call) + { + if (Msg->type == Type && Msg->status == 'N') + { +// Debugprintf("Comparing RMS Call %s %s", Msg->to, Call); + if (_stricmp(Msg->to, Call) == 0) + if (Msg->status == 'N' && Msg->type == Type && Msg->length <= MaxLen) + goto Forwardit; + else + Debugprintf("Call Match but Wrong Type/Len %c %d", Msg->type, Msg->length); + } + Call = conn->PacLinkCalls[index++]; + } +// continue; + } + + if (Msg->type == Type && Msg->length <= MaxLen && (Msg->status != 'H') + && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + // Message to be sent - do a consistancy check (State, etc) + + Forwardit: + + if (Msg->Defered > 0) // = response received + { + Msg->Defered--; + Debugprintf("Message %d deferred", Msg->number); + continue; + } + + if ((Msg->from[0] == 0) || (Msg->to[0] == 0)) + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Missing From: or To: field"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + Msg->status = 'H'; + continue; + } + + conn->NextMessagetoForward = m + 1; // So we don't offer again if defered + + Msg->Locked = 1; // So other MPS can't pick it up + + // if FBB forwarding add to list, eise save pointer + + if (conn->BBSFlags & FBBForwarding) + { + struct tm *tm; + time_t temp; + + FBBHeader = &conn->FBBHeaders[conn->FBBIndex++]; + + FBBHeader->FwdMsg = Msg; + FBBHeader->MsgType = Msg->type; + FBBHeader->Size = Msg->length; + TotalSize += Msg->length; + strcpy(FBBHeader->From, Msg->from); + strcpy(FBBHeader->To, Msg->to); + strcpy(FBBHeader->ATBBS, Msg->via); + strcpy(FBBHeader->BID, Msg->bid); + + // Set up R:Line, so se can add its length to the sise + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + FBBHeader->Size += sprintf_s(RLine, sizeof(RLine),"R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + // If using B2 forwarding we need the message size and Compressed size for FC proposal + + if (conn->BBSFlags & FBBB2Mode) + { + if (CreateB2Message(conn, FBBHeader, RLine) == FALSE) + { + char * MailBuffer = malloc(100); + char Title[100]; + int Length; + + // Corrupt B2 Message + + Debugprintf("Corrupt B2 Message found - Message %d will be held", Msg->number); + Msg->status = 'H'; + SaveMessageDatabase(); + + conn->FBBIndex--; + TotalSize -= Msg->length; + memset(&conn->FBBHeaders[conn->FBBIndex], 0, sizeof(struct FBBHeaderLine)); + + Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Corrupt B2 Message"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + continue; + } + } + + if (conn->FBBIndex == 5 || TotalSize > user->ForwardingInfo->MaxFBBBlockSize) + return TRUE; // Got max number or too big + + Found = TRUE; // Remember we have some + } + else + { + conn->FwdMsg = Msg; + return TRUE; + } + } + } + + return Found; +} + +BOOL SeeifMessagestoForward (int BBSNumber, CIRCUIT * conn) +{ + // See if any messages are queued for this BBS + + // if Conn is not NULL, also check Msg Type + + int m; + struct MsgInfo * Msg; + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + if (conn) + { + char Type = Msg->type; + + if ((conn->SendB && Type == 'B') || (conn->SendP && Type == 'P') || (conn->SendT && Type == 'T')) + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + else + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + } + + return FALSE; +} + +int CountMessagestoForward (struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + n++; + continue; // So we dont count twice in Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + n++; + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + } + } + } + + return n; +} + +int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; // So we dont count twice in Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + } + } + } + + return n; +} + +VOID SendWarningToSYSOP(struct MsgInfo * Msg) +{ + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Warning - Message %d has nowhere to go", Msg->number); + sprintf(Title, "Warning - Message %d has nowhere to go", Msg->number); + SendMessageToSYSOP(Title, MailBuffer, Length); +} + + + +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, "SYSTEM"); + if (SendSYStoSYSOPCall) + strcpy(Msg->to, SYSOPCall); + else + strcpy(Msg->to, "SYSOP"); + + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +VOID CheckBBSNumber(int i) +{ + // Make sure number is unique + + int Count = 0; + struct UserInfo * user; + + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + { + Count++; + + if (Count > 1) + { + // Second with same number - Renumber this one + + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + + Logprintf(LOG_BBS, NULL, '?', "Duplicate BBS Number found. BBS %s Old BBSNumber %d New BBS Number %d", user->Call, i, user->BBSNumber); + + } + } + } +} + + +int FindFreeBBSNumber() +{ + // returns the lowest number not used by any bbs or message. + + struct MsgInfo * Msg; + struct UserInfo * user; + int i, m; + + for (i = 1; i<= NBBBS; i++) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + goto nexti; // In use + } + + // Not used by BBS - check messages + + for (m = 1; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if (check_fwd_bit(Msg->fbbs, i)) + goto nexti; // In use + + if (check_fwd_bit(Msg->forw, i)) + goto nexti; // In use + } + + // Not in Use + + return i; + +nexti:; + + } + + return 0; // All used +} + +BOOL SetupNewBBS(struct UserInfo * user) +{ + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + return FALSE; + + user->BBSNext = BBSChain; + BBSChain = user; + + SortBBSChain(); + + ReinitializeFWDStruct(user); + + return TRUE; +} + +VOID DeleteBBS(struct UserInfo * user) +{ + struct UserInfo * BBSRec, * PrevBBS = NULL; + +#ifndef LINBPQ + RemoveMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, MF_BYCOMMAND); +#endif + for (BBSRec = BBSChain; BBSRec; PrevBBS = BBSRec, BBSRec = BBSRec->BBSNext) + { + if (user == BBSRec) + { + if (PrevBBS == NULL) // First in chain; + { + BBSChain = BBSRec->BBSNext; + break; + } + PrevBBS->BBSNext = BBSRec->BBSNext; + break; + } + } +} + + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo); + +VOID SetupForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + + char Key[100] = "BBSForwarding."; + char Temp[100]; + + HKEY hKey=0; + char RegKey[100] = "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\BBSForwarding\\"; + + int m; + struct MsgInfo * Msg; + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + if (UsingingRegConfig == 0) + { + // Config from file + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + strcat(Key, "*"); + + strcat(Key, user->Call); + + group = config_lookup (&cfg, Key); + + if (group == NULL) // No info + return; + else + { + ForwardingInfo->TOCalls = GetMultiStringValue(group, "TOCalls"); + ForwardingInfo->ConnectScript = GetMultiStringValue(group, "ConnectScript"); + ForwardingInfo->ATCalls = GetMultiStringValue(group, "ATCalls"); + ForwardingInfo->Haddresses = GetMultiStringValue(group, "HRoutes"); + ForwardingInfo->HaddressesP = GetMultiStringValue(group, "HRoutesP"); + ForwardingInfo->FWDTimes = GetMultiStringValue(group, "FWDTimes"); + + ForwardingInfo->Enabled = GetIntValue(group, "Enabled"); + ForwardingInfo->ReverseFlag = GetIntValue(group, "RequestReverse"); + ForwardingInfo->AllowBlocked = GetIntValue(group, "AllowBlocked"); + ForwardingInfo->AllowCompressed = GetIntValue(group, "AllowCompressed"); + ForwardingInfo->AllowB1 = GetIntValue(group, "UseB1Protocol"); + ForwardingInfo->AllowB2 = GetIntValue(group, "UseB2Protocol"); + ForwardingInfo->SendCTRLZ = GetIntValue(group, "SendCTRLZ"); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + if (ForwardingInfo->AllowCompressed) + ForwardingInfo->AllowBlocked = TRUE; + + ForwardingInfo->PersonalOnly = GetIntValue(group, "FWDPersonalsOnly"); + ForwardingInfo->SendNew = GetIntValue(group, "FWDNewImmediately"); + ForwardingInfo->FwdInterval = GetIntValue(group, "FwdInterval"); + ForwardingInfo->RevFwdInterval = GetIntValue(group, "RevFWDInterval"); + ForwardingInfo->MaxFBBBlockSize = GetIntValue(group, "MaxFBBBlock"); + ForwardingInfo->ConTimeout = GetIntValue(group, "ConTimeout"); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + if (ForwardingInfo->ConTimeout == 0) + ForwardingInfo->ConTimeout = 120; + + GetStringValue(group, "BBSHA", Temp); + + if (Temp[0]) + ForwardingInfo->BBSHA = _strdup(Temp); + else + ForwardingInfo->BBSHA = _strdup(""); + } + } + else + { +#ifndef LINBPQ + + int retCode,Type,Vallen; + + strcat(RegKey, user->Call); + retCode = RegOpenKeyEx (REGTREE, RegKey, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode != ERROR_SUCCESS) + return; + else + { + ForwardingInfo->ConnectScript = RegGetMultiStringValue(hKey, "Connect Script"); + ForwardingInfo->TOCalls = RegGetMultiStringValue(hKey, "TOCalls"); + ForwardingInfo->ATCalls = RegGetMultiStringValue(hKey, "ATCalls"); + ForwardingInfo->Haddresses = RegGetMultiStringValue(hKey, "HRoutes"); + ForwardingInfo->HaddressesP = RegGetMultiStringValue(hKey, "HRoutesP"); + ForwardingInfo->FWDTimes = RegGetMultiStringValue(hKey, "FWD Times"); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Enabled", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->Enabled,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "RequestReverse", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->ReverseFlag,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "AllowCompressed", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowCompressed,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B1 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB1,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B2 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB2,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "SendCTRLZ", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendCTRLZ,(ULONG *)&Vallen); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD Personals Only", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->PersonalOnly,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD New Immediately", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendNew,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"FWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->FwdInterval,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"RevFWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->RevFwdInterval,(ULONG *)&Vallen); + + RegQueryValueEx(hKey,"MaxFBBBlock",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->MaxFBBBlockSize,(ULONG *)&Vallen); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + Vallen=0; + retCode = RegQueryValueEx(hKey,"BBSHA",0 , (ULONG *)&Type,NULL, (ULONG *)&Vallen); + + if (retCode != 0) + { + // No Key - Get from WP?? + + WPRec * ptr = LookupWP(user->Call); + + if (ptr) + { + if (ptr->first_homebbs) + { + ForwardingInfo->BBSHA = _strdup(ptr->first_homebbs); + } + } + } + + if (Vallen) + { + ForwardingInfo->BBSHA = malloc(Vallen); + RegQueryValueEx(hKey, "BBSHA", 0, (ULONG *)&Type, ForwardingInfo->BBSHA,(ULONG *)&Vallen); + } + + RegCloseKey(hKey); + } +#endif + } + + // Convert FWD Times and H Addresses + + if (ForwardingInfo->FWDTimes) + SetupFwdTimes(ForwardingInfo); + + if (ForwardingInfo->Haddresses) + SetupHAddreses(ForwardingInfo); + + if (ForwardingInfo->HaddressesP) + SetupHAddresesP(ForwardingInfo); + + if (ForwardingInfo->BBSHA) + { + if (ForwardingInfo->BBSHA[0]) + SetupHAElements(ForwardingInfo); + else + { + free(ForwardingInfo->BBSHA); + ForwardingInfo->BBSHA = NULL; + } + } + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If any forward bits are set, increment count on BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount++; + } + } + } +} + +VOID * GetMultiStringValue(config_setting_t * group, char * ValueName) +{ + char * ptr1; + char * MultiString = NULL; + const char * ptr; + int Count = 0; + char ** Value; + config_setting_t *setting; + char * Save; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + setting = config_setting_get_member (group, ValueName); + + if (setting) + { + ptr = config_setting_get_string (setting); + + Save = _strdup(ptr); // DOnt want to change config string + ptr = Save; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) // ignore null elements + { + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + free(Save); + } + + Value[Count] = NULL; + return Value; +} + + +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName) +{ +#ifdef LINBPQ + return NULL; +#else + int retCode,Type,Vallen; + char * MultiString = NULL; + int ptr, len; + int Count = 0; + char ** Value; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + + Value[0] = NULL; + + Vallen=0; + + + retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if ((retCode != 0) || (Vallen < 3)) // Two nulls means empty multistring + { + free(Value); + return FALSE; + } + + MultiString = malloc(Vallen); + + retCode = RegQueryValueEx(hKey, ValueName, 0, + (ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen); + + ptr=0; + + while (MultiString[ptr]) + { + len=strlen(&MultiString[ptr]); + + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(&MultiString[ptr]); + ptr+= (len + 1); + } + + Value[Count] = NULL; + + free(MultiString); + + return Value; +#endif +} + +VOID FreeForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + int i; + + + ForwardingInfo = user->ForwardingInfo; + + FreeList(ForwardingInfo->TOCalls); + FreeList(ForwardingInfo->ATCalls); + FreeList(ForwardingInfo->Haddresses); + FreeList(ForwardingInfo->HaddressesP); + + i=0; + if(ForwardingInfo->HADDRS) + { + while(ForwardingInfo->HADDRS[i]) + { + FreeList(ForwardingInfo->HADDRS[i]); + i++; + } + free(ForwardingInfo->HADDRS); + free(ForwardingInfo->HADDROffet); + } + + i=0; + if(ForwardingInfo->HADDRSP) + { + while(ForwardingInfo->HADDRSP[i]) + { + FreeList(ForwardingInfo->HADDRSP[i]); + i++; + } + free(ForwardingInfo->HADDRSP); + } + + FreeList(ForwardingInfo->ConnectScript); + FreeList(ForwardingInfo->FWDTimes); + if (ForwardingInfo->FWDBands) + { + i=0; + while(ForwardingInfo->FWDBands[i]) + { + free(ForwardingInfo->FWDBands[i]); + i++; + } + free(ForwardingInfo->FWDBands); + } + if (ForwardingInfo->BBSHAElements) + { + i=0; + while(ForwardingInfo->BBSHAElements[i]) + { + free(ForwardingInfo->BBSHAElements[i]); + i++; + } + free(ForwardingInfo->BBSHAElements); + } + free(ForwardingInfo->BBSHA); + +} + +VOID FreeList(char ** Hddr) +{ + VOID ** Save; + + if (Hddr) + { + Save = (void **)Hddr; + while(Hddr[0]) + { + free(Hddr[0]); + Hddr++; + } + free(Save); + } +} + + +VOID ReinitializeFWDStruct(struct UserInfo * user) +{ + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + SetupForwardingStruct(user); + +} + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Times = ForwardingInfo->FWDTimes; + int Start, End; + int Count = 0; + + ForwardingInfo->FWDBands = zalloc(sizeof(struct FWDBAND)); + + if (Times) + { + while(Times[0]) + { + ForwardingInfo->FWDBands = realloc(ForwardingInfo->FWDBands, (Count+2)* sizeof(struct FWDBAND)); + ForwardingInfo->FWDBands[Count] = zalloc(sizeof(struct FWDBAND)); + + Start = atoi(Times[0]); + End = atoi(&Times[0][5]); + + ForwardingInfo->FWDBands[Count]->FWDStartBand = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + ForwardingInfo->FWDBands[Count]->FWDEndBand = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + Count++; + Times++; + } + ForwardingInfo->FWDBands[Count] = NULL; + } +} +void StartForwarding(int BBSNumber, char ** TempScript) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + + if ((BBSNumber == 0) || (user->BBSNumber == BBSNumber)) + if (ForwardingInfo) + if (ForwardingInfo->Enabled || BBSNumber) // Menu Command overrides enable + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + if (BBSNumber || SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) // Menu Command overrides Reverse + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + // See if TempScript requested + + if (user->ForwardingInfo->TempConnectScript) + FreeList(user->ForwardingInfo->TempConnectScript); + + user->ForwardingInfo->TempConnectScript = TempScript; + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + + return; +} + +size_t fwritex(CIRCUIT * conn, void * _Str, size_t _Size, size_t _Count, FILE * _File) +{ + if (_File) + return fwrite(_Str, _Size, _Count, _File); + + // Appending to MailBuffer + + memcpy(&conn->MailBuffer[conn->InputLen], _Str, _Count); + conn->InputLen += (int)_Count; + + return _Count; +} + + +BOOL ForwardMessagestoFile(CIRCUIT * conn, char * FN) +{ + BOOL AddCRLF = FALSE; + BOOL AutoImport = FALSE; + FILE * Handle = NULL; + char * Context; + BOOL Email = FALSE; + time_t now = time(NULL); + char * param; + + FN = strtok_s(FN, " ,", &Context); + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + } + } + // If FN is an email address, write to a temp file, and send via rms or emali gateway + + if (strchr(FN, '@') || _memicmp(FN, "RMS:", 4) == 0) + { + Email = TRUE; + AddCRLF =TRUE; + conn->MailBuffer=malloc(100000); + conn->MailBufferSize=100000; + conn->InputLen = 0; + } + else + { + Handle = fopen(FN, "ab"); + + if (Handle == NULL) + { + int err = GetLastError(); + Logprintf(LOG_BBS, conn, '!', "Failed to open Export File %s", FN); + return FALSE; + } + } + + while (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + struct tm * tm; + time_t temp; + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + char * MsgPtr; + char Line[256]; + int len; + struct UserInfo * user = conn->UserPointer; + int Index = 0; + + Msg = conn->FwdMsg; + + if (Email) + if (conn->InputLen + Msg->length + 500 > conn->MailBufferSize) + break; + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwritex(conn, Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwritex(conn, Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr = strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + fwritex(conn, Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwritex(conn, "\r\n", 1, 2, Handle); + + fwritex(conn, MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwritex(conn, "/EX\r\n", 1, 5, Handle); + else + fwritex(conn, "\r\n/EX\r\n", 1, 7, Handle); + + if (AddCRLF) + fwritex(conn, "\r\n", 1, 2, Handle); + + free(MsgBytes); + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + Msg->datechanged = now; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->UserPointer->ForwardingInfo->MsgCount--; + } + + if (Email) + { + struct MsgInfo * Msg; + BIDRec * BIDRec; + + if (conn->InputLen == 0) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + return TRUE; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datecreated = Msg->datechanged = Msg->datereceived = now; + + strcpy(Msg->from, BBSName); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + if (AutoImport) + sprintf(Msg->title, "Batched messages for AutoImport from BBS %s", BBSName); + else + sprintf(Msg->title, "Batched messages from BBS %s", BBSName); + + Msg->length = conn->InputLen; + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (_memicmp(FN, "SMTP:", 5) == 0) + { + strcpy(Msg->via, &FN[5]); + SMTPMsgCreated=TRUE; + } + else + { + strcpy(Msg->to, "RMS"); + if (_memicmp(FN, "RMS:", 4) == 0) + strcpy(Msg->via, &FN[4]); + else + strcpy(Msg->via, FN); + } + + MatchMessagetoBBSList(Msg, conn); + + SaveMessageDatabase(); + SaveBIDDatabase(); + } + else + fclose(Handle); + + SaveMessageDatabase(); + return TRUE; +} + +BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle) +{ + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(Msg->number); + char * MsgPtr; + char Line[256]; + int len; + int MsgLen = Msg->length; + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwrite(Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwrite(Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + MsgLen = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + + // If a B2 Message, remove B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + fwrite(Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwrite("\r\n", 1, 2, Handle); + + fwrite(MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwrite("/EX\r\n", 1, 5, Handle); + else + fwrite("\r\n/EX\r\n", 1, 7, Handle); + + free(MsgBytes); + + return TRUE; + +} + +BOOL ConnecttoBBS (struct UserInfo * user) +{ + int n, p; + CIRCUIT * conn; + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + + for (n = NumberofStreams-1; n >= 0 ; n--) + { + conn = &Connections[n]; + + if (conn->Active == FALSE) + { + p = conn->BPQStream; + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->BPQStream = p; + + // Can't set Active until Connected or Stuck Session detertor can clear session. + // But must set Active before Connected() runs or will appear is Incoming Connect. + // Connected() is semaphored, so get semaphore before ConnectUsingAppl + // Probably better to semaphore lost session code instead + + + strcpy(conn->Callsign, user->Call); + conn->BBSFlags |= (RunningConnectScript | OUTWARDCONNECT); + conn->UserPointer = user; + + Logprintf(LOG_BBS, conn, '|', "Connecting to BBS %s", user->Call); + + ForwardingInfo->MoreLines = TRUE; + + GetSemaphore(&ConSemaphore, 1); + conn->Active = TRUE; + ConnectUsingAppl(conn->BPQStream, BBSApplMask); + FreeSemaphore(&ConSemaphore); + + // If we are sending to a dump pms we may need to connect using the message sender's callsign. + // But we wont know until we run the connect script, which is a bit late to change call. Could add + // flag to forwarding config, but easier to look for SETCALLTOSENDER in the connect script. + + if (strstr(ForwardingInfo->ConnectScript[0], "SETCALLTOSENDER")) + { + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + // We have a message to send + + struct MsgInfo * Msg; + unsigned char AXCall[7]; + + Msg = conn->FwdMsg; + ConvToAX25(Msg->from, AXCall); + ChangeSessionCallsign(p, AXCall); + + conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER | NEWPACCOM; + conn->NextMessagetoForward = 0; // was set by FindMessages + } + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + } +#ifdef LINBPQ + { + BPQVECSTRUC * SESS; + SESS = &BPQHOSTVECTOR[conn->BPQStream - 1]; + + if (SESS->HOSTSESSION == NULL) + { + Logprintf(LOG_BBS, NULL, '|', "No L4 Sessions for connect to BBS %s", user->Call); + return FALSE; + } + + SESS->HOSTSESSION->Secure_Session = 1; + } +#endif + + strcpy(conn->Callsign, user->Call); + + // Connected Event will trigger connect to remote system + + RefreshMainWindow(); + + return TRUE; + } + } + + Logprintf(LOG_BBS, NULL, '|', "No Free Streams for connect to BBS %s", user->Call); + + return FALSE; + +} + +struct DelayParam +{ + struct UserInfo * User; + CIRCUIT * conn; + int Delay; +}; + +struct DelayParam DParam; // Not 100% safe, but near enough + +VOID ConnectDelayThread(struct DelayParam * DParam) +{ + struct UserInfo * User = DParam->User; + int Delay = DParam->Delay; + + User->ForwardingInfo->Forwarding = TRUE; // Minimize window for two connects + + Sleep(Delay); + + User->ForwardingInfo->Forwarding = TRUE; + ConnecttoBBS(User); + + return; +} + +VOID ConnectPauseThread(struct DelayParam * DParam) +{ + CIRCUIT * conn = DParam->conn; + int Delay = DParam->Delay; + char Msg[] = "Pause Ok\r "; + + Sleep(Delay); + + ProcessBBSConnectScript(conn, Msg, 9); + + return; +} + + +/* +BOOL ProcessBBSConnectScriptInner(CIRCUIT * conn, char * Buffer, int len); + + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + BOOL Ret; + GetSemaphore(&ScriptSEM); + Ret = ProcessBBSConnectScriptInner(conn, Buffer, len); + FreeSemaphore(&ScriptSEM); + + return Ret; +} +*/ + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + char callsign[10]; + int port, sesstype, paclen, maxframe, l4window; + char * ptr, * ptr2; + + WriteLogLine(conn, '<',Buffer, len-1, LOG_BBS); + + Buffer[len]=0; + _strupr(Buffer); + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + if (ForwardingInfo->ScriptIndex == -1) + { + // First Entry - if first line is TIMES, check and skip forward if necessary + + int n = 0; + int Start, End; + time_t now = time(NULL), StartSecs, EndSecs; + char * Line; + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; + Line = Scripts[n]; + + if (_memicmp(Line, "TIMES", 5) == 0) + { + NextBand: + Start = atoi(&Line[6]); + End = atoi(&Line[11]); + + StartSecs = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + EndSecs = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + if ((StartSecs <= now) && (EndSecs >= now)) + goto InBand; // In band + + // Look for next TIME + NextLine: + Line = Scripts[++n]; + + if (Line == NULL) + { + // No more lines - Disconnect + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Line, "TIMES", 5) != 0) + goto NextLine; + else + goto NextBand; +InBand: + ForwardingInfo->ScriptIndex = n; + } + + } + else + { + // Dont check first time through + + if (strcmp(Buffer, "*** CONNECTED ") != 0) + { + if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + { + ForwardingInfo->MoreLines = FALSE; + } + if (!ForwardingInfo->MoreLines) + goto CheckForSID; + } + } + + if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || + (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || + strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || + strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || + strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || + strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) + { + // Connect Failed + + char * Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + int Delay = 1000; + + // Look for an alternative connect block (Starting with ELSE) + + ElseLoop: + + // Skip any comments + + while (Cmd && ((strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#'))) + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // TIMES terminates a script + + if (Cmd == 0 || _memicmp(Cmd, "TIMES", 5) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "ELSE", 4) != 0) + { + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + goto ElseLoop; + } + + if (_memicmp(&Cmd[5], "DELAY", 5) == 0) + Delay = atoi(&Cmd[10]) * 1000; + else + Delay = 1000; + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + + DParam.Delay = Delay; + DParam.User = conn->UserPointer; + + _beginthread((void (*)(void *))ConnectDelayThread, 0, &DParam); + + return FALSE; + } + + // The pointer is only updated when we get the connect, so we can tell when the last line is acked + // The first entry is always from Connected event, so don't have to worry about testing entry -1 below + + + // NETROM to KA node returns + + //c 1 milsw + //WIRAC:N9PMO-2} Connected to MILSW + //###CONNECTED TO NODE MILSW(N9ZXS) CHANNEL A + //You have reached N9ZXS's KA-Node MILSW + //ENTER COMMAND: B,C,J,N, or Help ? + + //C KB9PRF-7 + //###LINK MADE + //###CONNECTED TO NODE KB9PRF-7(KB9PRF-4) CHANNEL A + + // Look for (Space)Connected so we aren't fooled by ###CONNECTED TO NODE, which is not + // an indication of a connect. + + if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) + { + // If connected to SYNC, save IP address and port + + char * Cmd; + + if (strstr(Buffer, "*** CONNECTED TO SYNC")) + { + char * IPAddr = &Buffer[22]; + char * Port = strlop(IPAddr, ':'); + + if (Port) + { + if (conn->SyncHost) + free(conn->SyncHost); + + conn->SyncHost = _strdup(IPAddr); + conn->SyncPort = atoi(Port); + } + } + + if (conn->SkipConn) + { + conn->SkipConn = FALSE; + return TRUE; + } + + LoopBack: + + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // Only Check until script is finished + + if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) + goto LoopBack; // Blank line + + if (Cmd && _memicmp(Cmd, "TIMES", 5) != 0 && _memicmp(Cmd, "ELSE", 4) != 0) // Only Check until script is finished + { + if (_memicmp(Cmd, "MSGTYPE", 7) == 0) + { + char * ptr; + + // Select Types to send. Only send types in param. Only reverse if R in param + + _strupr(Cmd); + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + + strcpy(conn->MSGTYPES, &Cmd[8]); + + if (strchr(&Cmd[8], 'R')) conn->DoReverse = TRUE; + + ptr = strchr(&Cmd[8], 'B'); + + if (ptr) + { + conn->SendB = TRUE; + conn->MaxBLen = atoi(++ptr); + if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; + } + + ptr = strchr(&Cmd[8], 'T'); + + if (ptr) + { + conn->SendT = TRUE; + conn->MaxTLen = atoi(++ptr); + if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; + } + ptr = strchr(&Cmd[8], 'P'); + + if (ptr) + { + conn->SendP = TRUE; + conn->MaxPLen = atoi(++ptr); + if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; + } + + // If nothing to do, terminate script + + if (conn->DoReverse || SeeifMessagestoForward(conn->UserPointer->BBSNumber, conn)) + goto LoopBack; + + Logprintf(LOG_BBS, conn, '?', "Nothing to do - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "INTERLOCK ", 10) == 0) + { + // Used to limit connects on a port to 1 + + int Port; + char Option[80]; + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + sscanf(&Cmd[10], "%d %s", &Port, &Option[0]); + + if (CountConnectionsOnPort(Port)) + { + Logprintf(LOG_BBS, conn, '?', "Interlocked Port is busy - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + goto LoopBack; + } + + if (_memicmp(Cmd, "RADIO AUTH", 10) == 0) + { + // Generate a Password to enable RADIO commands on a remote node + char AuthCommand[80]; + + _strupr(Cmd); + strcpy(AuthCommand, Cmd); + + CreateOneTimePassword(&AuthCommand[11], &Cmd[11], 0); + + nodeprintf(conn, "%s\r", AuthCommand); + return TRUE; + } + + if (_memicmp(Cmd, "SKIPCON", 7) == 0) + { + // Remote Node sends Connected in CTEXT - we need to swallow it + + conn->SkipConn = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SendWL2KPM", 10) == 0|| _memicmp(Cmd, "SendWL2KFW", 10) == 0) + { + // Send ;FW: command + + conn->SendWL2KFW = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SKIPPROMPT", 10) == 0) + { + // Remote Node sends > at end of CTEXT - we need to swallow it + + conn->SkipPrompt++; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "TEXTFORWARDING", 10) == 0) + { + conn->BBSFlags |= TEXTFORWARDING; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SETCALLTOSENDER", 15) == 0) + { + conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "RADIOONLY", 9) == 0) + { + conn->BBSFlags |= WINLINKRO; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SYNC", 4) == 0) + { + conn->BBSFlags |= SYNCMODE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "NEEDLF", 6) == 0) + { + conn->BBSFlags |= NEEDLF; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "MCASTRX", 6) == 0) + { + conn->BBSFlags |= MCASTRX; + conn->MCastListenTime = atoi(&Cmd[7]) * 6; // Time to run session for *6 as value is mins put timer ticks 10 secs + + // send MCAST to Node + + nodeprintfEx(conn, "MCAST\r"); + return TRUE; + } + + if (_memicmp(Cmd, "FLARQ", 5) == 0) + { + conn->BBSFlags |= FLARQMAIL; + + CheckForEnd: + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + ForwardingInfo->MoreLines = FALSE; + + goto LoopBack; + } + if (_memicmp(Cmd, "PAUSE", 5) == 0) + { + // Pause script + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + DParam.Delay = atoi(&Cmd[6]) * 1000; + DParam.conn = conn; + + _beginthread((void (*)(void *))ConnectPauseThread, 0, &DParam); + + return TRUE; + } + + if (_memicmp(Cmd, "FILE", 4) == 0) + { + if (Cmd[4] == 0) + { + // Missing Filename + + Logprintf(LOG_BBS, conn, '!', "Export file name missing"); + } + else + ForwardMessagestoFile(conn, &Cmd[5]); + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "SMTP", 4) == 0) + { + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + + SendAMPRSMTP(conn); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + + if (_memicmp(Cmd, "IMPORT", 6) == 0) + { + char * File, * Context; + int Num; + char * Temp = _strdup(Cmd); + + File = strtok_s(&Temp[6], " ", &Context); + + if (File && File[0]) + { + Num = ImportMessages(NULL, File, TRUE); + + Logprintf(LOG_BBS, NULL, '|', "Imported %d Message(s) from %s", Num, File); + + if (Context && _stricmp(Context, "delete") == 0) + DeleteFile(File); + } + free(Temp); + + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + goto LoopBack; + } + + // Anything else is sent to Node + + // Replace \ with # so can send commands starting with # + + if (Cmd[0] == '\\') + { + Cmd[0] = '#'; + nodeprintfEx(conn, "%s\r", Cmd); + Cmd[0] = '\\'; // Put \ back in script + } + else + nodeprintfEx(conn, "%s\r", Cmd); + + return TRUE; + } + + // End of script. + + ForwardingInfo->MoreLines = FALSE; + + if (conn->BBSFlags & MCASTRX) + { + // No session with Multicast, so no SID + + conn->BBSFlags &= ~RunningConnectScript; + return TRUE; + } + + if (conn->BBSFlags & FLARQMAIL) + { + // FLARQ doesnt send a prompt - Just send message(es) + + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + // Update Paclen + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + SendARQMail(conn); + return TRUE; + } + + + return TRUE; + } + + ptr = strchr(Buffer, '}'); + + if (ptr && ForwardingInfo->MoreLines) // Beware it could be part of ctext + { + // Could be respsonse to Node Command + + ptr+=2; + + ptr2 = strchr(&ptr[0], ' '); + + if (ptr2) + { + if (_memicmp(ptr, Scripts[ForwardingInfo->ScriptIndex], ptr2-ptr) == 0) // Reply to last sscript command + { + if (Scripts[ForwardingInfo->ScriptIndex+1] && _memicmp(Scripts[ForwardingInfo->ScriptIndex+1], "else", 4) == 0) + { + // stray match or misconfigured + + return TRUE; + } + + ForwardingInfo->ScriptIndex++; + + if (Scripts[ForwardingInfo->ScriptIndex]) + if (_memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) != 0) + nodeprintf(conn, "%s\r", Scripts[ForwardingInfo->ScriptIndex]); + + return TRUE; + } + } + } + + // Not Success or Fail. If last line is still outstanding, wait fot Response + // else look for SID or Prompt + + if (conn->SkipPrompt && Buffer[len-2] == '>') + { + conn->SkipPrompt--; + return TRUE; + } + + if (ForwardingInfo->MoreLines) + return TRUE; + + // No more steps, Look for SID or Prompt + +CheckForSID: + + if (strstr(Buffer, "POSYNCHELLO")) // RMS RELAY Sync process + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + ForwardingInfo->LastReverseForward = time(NULL); + + ProcessLine(conn, 0, Buffer, len); + return FALSE; + } + + if (strstr(Buffer, "SORRY, NO")) // URONODE + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (memcmp(Buffer, ";PQ: ", 5) == 0) + { + // Secure CMS challenge + + int Len; + struct UserInfo * User = conn->UserPointer; + char * Pass = User->CMSPass; + int Response ; + char RespString[12]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + + strlop(ConnectingCall, ' '); + + if (Pass[0] == 0) + { + Pass = User->pass; // Old Way + if (Pass[0] == 0) + { + strlop(ConnectingCall, '-'); + User = LookupCall(ConnectingCall); + if (User) + Pass = User->CMSPass; + } + } + + // + + Response = GetCMSHash(&Buffer[5], Pass); + + sprintf(RespString, "%010d", Response); + + Len = sprintf(conn->SecureMsg, ";PR: %s\r", &RespString[2]); + + // Save challengs in case needed for FW lines + + strcpy(conn->PQChallenge, &Buffer[5]); + + return FALSE; + } + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // Update PACLEN + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + conn->FBBChecksum = 0; + } + + return TRUE; + } + + if (memcmp(Buffer, "[PAKET ", 7) == 0) + { + conn->BBSFlags |= BBS; + conn->BBSFlags |= MBLFORWARDING; + } + + if (Buffer[len-2] == '>') + { + if (conn->SkipPrompt) + { + conn->SkipPrompt--; + return TRUE; + } + + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + if (memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING)) + { + // PK232. Don't send a SID, and switch to Text Mode + + conn->BBSFlags |= (BBS | TEXTFORWARDING); + conn->Flags |= SENDTITLE; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + Msg = conn->FwdMsg; + + if ((conn->BBSFlags & SETCALLTOSENDER)) + nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); + else + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + return TRUE; + } + + if (strcmp(conn->Callsign, "RMS") == 0 || conn->SendWL2KFW) + { + // Build a ;FW: line with all calls with PollRMS Set + + // According to Lee if you use secure login the first + // must be the BBS call + // Actually I don't think we need the first, + // as that is implied + + // If a secure password is available send the new + // call|response format. + + // I think this should use the session callsign, which + // normally will be the BBS ApplCall, and not the BBS Name, + // but coudl be changed by *** LINKED + + int i, s; + char FWLine[10000] = ";FW: "; + struct UserInfo * user; + char RMSCall[20]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + strlop(ConnectingCall, ' '); + + strcat (FWLine, ConnectingCall); + + for (i = 0; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1; + + for (s = 0; s < 16; s++) + { + if (user->RMSSSIDBits & (1 << s)) + { + if (s) + sprintf(RMSCall, "%s-%d", user->Call, s); + else + sprintf(RMSCall, "%s", user->Call); + + // We added connectingcall at front + + if (strcmp(RMSCall, ConnectingCall) != 0) + { + strcat(FWLine, " "); + strcat(FWLine, RMSCall); + + if (user->CMSPass[0]) + { + int Response = GetCMSHash(conn->PQChallenge, user->CMSPass); + char RespString[12]; + + sprintf(RespString, "%010d", Response); + strcat(FWLine, "|"); + strcat(FWLine, &RespString[2]); + } + } + } + } + } + } + + strcat(FWLine, "\r"); + + nodeprintf(conn, FWLine); + } + + // Only declare B1 and B2 if other end did, and we are configued for it + + nodeprintfEx(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + (conn->BBSFlags & FBBCompressed) ? "B" : "", + (conn->BBSFlags & FBBB1Mode && !(conn->BBSFlags & FBBB2Mode)) ? "1" : "", + (conn->BBSFlags & FBBB2Mode) ? "2" : "", + (conn->BBSFlags & FBBForwarding) ? "F" : "", + (conn->BBSFlags & WINLINKRO) ? "" : "J"); + + if (conn->SecureMsg[0]) + { + struct UserInfo * user; + BBSputs(conn, conn->SecureMsg); + conn->SecureMsg[0] = 0; + + // Also send a Location Comment Line + + //; GM8BPQ-10 DE G8BPQ (IO92KX) + //; WL2K DE GM8BPQ () (PAT) + + user = LookupCall(BBSName); + + if (LOC && LOC[0]) + nodeprintf(conn, "; WL2K DE %s (%s)\r", BBSName, LOC); + } + + if (conn->BPQBBS && conn->MSGTYPES[0]) + + // Send a ; MSGTYPES to control what he sends us + + nodeprintf(conn, "; MSGTYPES %s\r", conn->MSGTYPES); + + if (conn->BBSFlags & FBBForwarding) + { + if (!FBBDoForward(conn)) // Send proposal if anthing to forward + { + if (conn->DoReverse) + FBBputs(conn, "FF\r"); + else + { + FBBputs(conn, "FQ\r"); + conn->CloseAfterFlush = 20; // 2 Secs + } + } + + return TRUE; + } + + return TRUE; + } + + return TRUE; +} + +VOID Parse_SID(CIRCUIT * conn, char * SID, int len) +{ + ChangeSessionIdletime(conn->BPQStream, BBSIDLETIME); // Default Idletime for BBS Sessions + + // scan backwards for first '-' + + if (strstr(SID, "BPQCHATSERVER")) + { + Disconnect(conn->BPQStream); + return; + } + + if (strstr(SID, "RMS Ex") || strstr(SID, "Winlink Ex")) + { + conn->RMSExpress = TRUE; + conn->Paclink = FALSE; + conn->PAT = FALSE; + + // Set new RMS Users as RMS User + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + + if (stristr(SID, "PAT")) + { + // Set new PAT Users as RMS User + + conn->RMSExpress = FALSE; + conn->Paclink = FALSE; + conn->PAT = TRUE; + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + if (strstr(SID, "Paclink")) + { + conn->RMSExpress = FALSE; + conn->Paclink = TRUE; + } + + if (strstr(SID, "WL2K-")) + { + conn->WL2K = TRUE; + conn->BBSFlags |= WINLINKRO; + } + + if (strstr(SID, "MFJ-")) + { + conn->BBSFlags |= MFJMODE; + } + + if (_memicmp(SID, "OpenBCM", 7) == 0) + { + // We should really only do this on Telnet Connections, as OpenBCM flag is used to remove relnet transparency + + + conn->OpenBCM = TRUE; + } + + if (_memicmp(SID, "PMS-3.2", 7) == 0) + { + // Paccom TNC that doesn't send newline prompt ater receiving subject + + conn->BBSFlags |= NEWPACCOM; + } + + // See if BPQ for selective forwarding + + if (strstr(SID, "BPQ")) + conn->BPQBBS = TRUE; + + while (len > 0) + { + switch (SID[len--]) + { + case '-': + + len=0; + break; + + case '$': + + conn->BBSFlags |= BBS | MBLFORWARDING; + conn->Paging = FALSE; + + break; + + case 'F': // FBB Blocked Forwarding + + // We now support blocked uncompressed. Not necessarily compatible with FBB + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + if (conn->UserPointer->ForwardingInfo->AllowBlocked) + { + conn->BBSFlags |= FBBForwarding | BBS; + conn->BBSFlags &= ~MBLFORWARDING; + + conn->Paging = FALSE; + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + // Allocate a Header Block + + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + } + break; + + case 'J': + + // Suspected to be associated with Winlink Radio Only + + conn->BBSFlags &= ~WINLINKRO; + break; + + case 'B': + + if (conn->UserPointer->ForwardingInfo->AllowCompressed) + { + conn->BBSFlags |= FBBCompressed; + conn->DontSaveRestartData = FALSE; // Allow restarts + + // Look for 1 or 2 or 12 as next 2 chars + + if (SID[len+2] == '1') + { + if (conn->UserPointer->ForwardingInfo->AllowB1 || + conn->UserPointer->ForwardingInfo->AllowB2) // B2 implies B1 + conn->BBSFlags |= FBBB1Mode; + + if (SID[len+3] == '2') + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + break; + } + + if (SID[len+2] == '2') + { + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + if (conn->UserPointer->ForwardingInfo->AllowB1) + conn->BBSFlags |= FBBB1Mode; // B2 should allow fallback to B1 (but RMS doesnt!) + + } + break; + } + + break; + } + } + + // Only allow blocked non-binary to other BPQ Nodes + + if ((conn->BBSFlags & FBBForwarding) && ((conn->BBSFlags & FBBCompressed) == 0) && (conn->BPQBBS == 0)) + { + // Switch back to MBL + + conn->BBSFlags |= MBLFORWARDING; + conn->BBSFlags &= ~FBBForwarding; // Turn off FBB Blocked + } + + return; +} + +VOID BBSSlowTimer() +{ + ConnectionInfo * conn; + int n; + + // Called every 10 seconds + + MCastTimer(); + + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + // Check for stuck BBS sessions (BBS session but no Node Session) + + int state; + + GetSemaphore(&ConSemaphore, 1); + SessionStateNoAck(conn->BPQStream, &state); + FreeSemaphore(&ConSemaphore); + + if (state == 0) // No Node Session + { + // is it safe just to clear Active ?? + + conn->InputMode = 0; // So Disconnect wont save partial transfer + conn->BBSFlags = 0; + Disconnected (conn->BPQStream); + continue; + } + + if (conn->BBSFlags & MCASTRX) + MCastConTimer(conn); + + + // Check SIDTImers - used to detect failure to compete SID Handshake + + if (conn->SIDResponseTimer) + { + conn->SIDResponseTimer--; + if (conn->SIDResponseTimer == 0) + { + // Disconnect Session + + Disconnect(conn->BPQStream); + } + } + } + } + + // Flush logs + + for (n = 0; n < 4; n++) + { + if (LogHandle[n]) + { + time_t LT = time(NULL); + if ((LT - LastLogTime[n]) > 30) + { + LastLogTime[n] = LT; + fclose(LogHandle[n]); + LogHandle[n] = NULL; + } + } + } +} + + +VOID FWDTimerProc() +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + ForwardingInfo->FwdTimer+=10; + + if (ForwardingInfo->FwdTimer >= ForwardingInfo->FwdInterval) + { + ForwardingInfo->FwdTimer=0; + + if (ForwardingInfo->FWDBands && ForwardingInfo->FWDBands[0]) + { + // Check Timebands + + struct FWDBAND ** Bands = ForwardingInfo->FWDBands; + int Count = 0; + time_t now = time(NULL); + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; // Secs in day + + while(Bands[Count]) + { + if ((Bands[Count]->FWDStartBand < now) && (Bands[Count]->FWDEndBand >= now)) + goto FWD; // In band + + Count++; + } + continue; // Out of bands + } + FWD: + if (ForwardingInfo->Enabled) + { + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + { + //Temp Debug Code + +// Debugprintf("ReverseFlag = %d, Msgs to Forward Flag %d Msgs to Forward Count %d", +// ForwardingInfo->ReverseFlag, +// SeeifMessagestoForward(user->BBSNumber, NULL), +// CountMessagestoForward(user)); + + if (SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) + + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + + // remove any old TempScript + + if (user->ForwardingInfo->TempConnectScript) + { + FreeList(user->ForwardingInfo->TempConnectScript); + user->ForwardingInfo->TempConnectScript = NULL; + } + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + } + } + } +} + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line) +{ + // ?? malloc and clear + + void * ptr; + +#ifdef WIN32 + ptr=_malloc_dbg(len, type, file, line); +#else + ptr = malloc(len); +#endif + if (ptr) + memset(ptr, 0, len); + + return ptr; +} + +struct MsgInfo * FindMessageByNumber(int msgno) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + if (Msg->number && Msg->number < msgno) // sometimes get zero msg number + return NULL; // Not found + + m--; + + } while (m > 0); + + return NULL; +} + +struct MsgInfo * FindMessageByBID(char * BID) +{ + int m = NumberofMessages; + + struct MsgInfo * Msg; + + while (m > 0) + { + Msg = MsgHddrPtr[m]; + + if (strcmp(Msg->bid, BID) == 0) + return Msg; + + m--; + } + + return NULL; +} + +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, j = 0, val1, val2; + unsigned char hostname[100]=""; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // String is now encoded as hex pairs, but still need to decode old format + + for (i=0; i < len; i++) + { + if (Encrypt[i] < '0' || Encrypt[i] > 'F') + goto OldFormat; + } + + // Only '0' to 'F' + + for (i=0; i < len; i++) + { + val1 = Encrypt[i++]; + val1 -= '0'; + if (val1 > 9) + val1 -= 7; + + val2 = Encrypt[i]; + val2 -= '0'; + if (val2 > 9) + val2 -= 7; + + Pass[j] = (val1 << 4) | val2; + Pass[j] ^= hash[j]; + j++; + } + + return; + +OldFormat: + + for (i=0; i < len; i++) + { + Pass[i] = Encrypt[i] ^ hash[i]; + } + + return; +} + +int EncryptPass(char * Pass, char * Encrypt) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, val; + unsigned char hostname[100]; + unsigned char extendedpass[100]; + unsigned int passlen; + unsigned char * ptr; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // if password is less than 16 chars, extend with zeros + + passlen=(int)strlen(Pass); + + strcpy(extendedpass, Pass); + + if (passlen < 16) + { + for (i=passlen+1; i <= 16; i++) + { + extendedpass[i] = 0; + } + + passlen = 16; + } + + ptr = Encrypt; + Encrypt[0] = 0; + + for (i=0; i < passlen; i++) + { + val = extendedpass[i] ^ hash[i]; + ptr += sprintf(ptr, "%02X", val); + } + + return passlen * 2; +} + + + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +VOID SaveInt64Value(config_setting_t * group, char * name, long long value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT64); + if(setting) + config_setting_set_int64(setting, value); +} + +VOID SaveFloatValue(config_setting_t * group, char * name, double value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_FLOAT); + if (setting) + config_setting_set_float(setting, value); +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + +VOID SaveOverride(config_setting_t * group, char * name, struct Override ** values) +{ + config_setting_t *setting; + struct Override ** Calls; + char Multi[10000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + ptr += sprintf(ptr, "%s, %d|", Calls[0]->Call, Calls[0]->Days); + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + + +VOID SaveMultiStringValue(config_setting_t * group, char * name, char ** values) +{ + config_setting_t *setting; + char ** Calls; + char Multi[100000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + strcpy(ptr, Calls[0]); + ptr += strlen(Calls[0]); + *(ptr++) = '|'; + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + +int configSaved = 0; + +VOID SaveConfig(char * ConfigName) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + config_setting_t *root, *group, *bbs; + int i; + char Size[80]; + struct BBSForwardingInfo DummyForwardingInfo; + char Line[1024]; + + if (configSaved == 0) + { + // only create backup once per run + + CopyConfigFile(ConfigName); + configSaved = 1; + } + + memset(&DummyForwardingInfo, 0, sizeof(struct BBSForwardingInfo)); + + // Get rid of old config before saving + + config_destroy(&cfg); + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "Streams", MaxStreams); + SaveIntValue(group, "BBSApplNum", BBSApplNum); + SaveStringValue(group, "BBSName", BBSName); + SaveStringValue(group, "SYSOPCall", SYSOPCall); + SaveStringValue(group, "H-Route", HRoute); + SaveStringValue(group, "AMPRDomain", AMPRDomain); + SaveIntValue(group, "EnableUI", EnableUI); + SaveIntValue(group, "RefuseBulls", RefuseBulls); + SaveIntValue(group, "OnlyKnown", OnlyKnown); + SaveIntValue(group, "SendSYStoSYSOPCall", SendSYStoSYSOPCall); + SaveIntValue(group, "SendBBStoSYSOPCall", SendBBStoSYSOPCall); + SaveIntValue(group, "DontHoldNewUsers", DontHoldNewUsers); + SaveIntValue(group, "DefaultNoWINLINK", DefaultNoWINLINK); + SaveIntValue(group, "AllowAnon", AllowAnon); + SaveIntValue(group, "DontNeedHomeBBS", DontNeedHomeBBS); + SaveIntValue(group, "DontCheckFromCall", DontCheckFromCall); + SaveIntValue(group, "UserCantKillT", UserCantKillT); + + SaveIntValue(group, "ForwardToMe", ForwardToMe); + SaveIntValue(group, "SMTPPort", SMTPInPort); + SaveIntValue(group, "POP3Port", POP3InPort); + SaveIntValue(group, "NNTPPort", NNTPInPort); + SaveIntValue(group, "RemoteEmail", RemoteEmail); + SaveIntValue(group, "SendAMPRDirect", SendAMPRDirect); + + SaveIntValue(group, "MailForInterval", MailForInterval); + SaveStringValue(group, "MailForText", MailForText); + + EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); + + SaveIntValue(group, "AuthenticateSMTP", SMTPAuthNeeded); + + SaveIntValue(group, "MulticastRX", MulticastRX); + + SaveIntValue(group, "SMTPGatewayEnabled", ISP_Gateway_Enabled); + SaveIntValue(group, "ISPSMTPPort", ISPSMTPPort); + SaveIntValue(group, "ISPPOP3Port", ISPPOP3Port); + SaveIntValue(group, "POP3PollingInterval", ISPPOP3Interval); + + SaveStringValue(group, "MyDomain", MyDomain); + SaveStringValue(group, "ISPSMTPName", ISPSMTPName); + SaveStringValue(group, "ISPEHLOName", ISPEHLOName); + SaveStringValue(group, "ISPPOP3Name", ISPPOP3Name); + SaveStringValue(group, "ISPAccountName", ISPAccountName); + SaveStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); + + + // Save Window Sizes + +#ifndef LINBPQ + + if (ConsoleRect.right) + { + sprintf(Size,"%d,%d,%d,%d",ConsoleRect.left, ConsoleRect.right, + ConsoleRect.top, ConsoleRect.bottom); + + SaveStringValue(group, "ConsoleSize", Size); + } + + sprintf(Size,"%d,%d,%d,%d,%d",MonitorRect.left,MonitorRect.right,MonitorRect.top,MonitorRect.bottom, hMonitor ? 1 : 0); + SaveStringValue(group, "MonitorSize", Size); + + sprintf(Size,"%d,%d,%d,%d",MainRect.left,MainRect.right,MainRect.top,MainRect.bottom); + SaveStringValue(group, "WindowSize", Size); + + SaveIntValue(group, "Bells", Bells); + SaveIntValue(group, "FlashOnBell", FlashOnBell); + SaveIntValue(group, "StripLF", StripLF); + SaveIntValue(group, "WarnWrap", WarnWrap); + SaveIntValue(group, "WrapInput", WrapInput); + SaveIntValue(group, "FlashOnConnect", FlashOnConnect); + SaveIntValue(group, "CloseWindowOnBye", CloseWindowOnBye); + +#endif + + SaveIntValue(group, "Log_BBS", LogBBS); + SaveIntValue(group, "Log_TCP", LogTCP); + + sprintf(Size,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + SaveStringValue(group, "Version", Size); + + // Save Welcome Messages and prompts + + SaveStringValue(group, "WelcomeMsg", WelcomeMsg); + SaveStringValue(group, "NewUserWelcomeMsg", NewWelcomeMsg); + SaveStringValue(group, "ExpertWelcomeMsg", ExpertWelcomeMsg); + + SaveStringValue(group, "Prompt", Prompt); + SaveStringValue(group, "NewUserPrompt", NewPrompt); + SaveStringValue(group, "ExpertPrompt", ExpertPrompt); + SaveStringValue(group, "SignoffMsg", SignoffMsg); + + SaveMultiStringValue(group, "RejFrom", RejFrom); + SaveMultiStringValue(group, "RejTo", RejTo); + SaveMultiStringValue(group, "RejAt", RejAt); + SaveMultiStringValue(group, "RejBID", RejBID); + + SaveMultiStringValue(group, "HoldFrom", HoldFrom); + SaveMultiStringValue(group, "HoldTo", HoldTo); + SaveMultiStringValue(group, "HoldAt", HoldAt); + SaveMultiStringValue(group, "HoldBID", HoldBID); + + SaveIntValue(group, "SendWP", SendWP); + SaveIntValue(group, "SendWPType", SendWPType); + SaveIntValue(group, "FilterWPBulls", FilterWPBulls); + SaveIntValue(group, "NoWPGuesses", NoWPGuesses); + + SaveStringValue(group, "SendWPTO", SendWPTO); + SaveStringValue(group, "SendWPVIA", SendWPVIA); + + SaveMultiStringValue(group, "SendWPAddrs", SendWPAddrs); + + // Save Forwarding Config + + // Interval and Max Sizes and Aliases are not user specific + + SaveIntValue(group, "MaxTXSize", MaxTXSize); + SaveIntValue(group, "MaxRXSize", MaxRXSize); + SaveIntValue(group, "ReaddressLocal", ReaddressLocal); + SaveIntValue(group, "ReaddressReceived", ReaddressReceived); + SaveIntValue(group, "WarnNoRoute", WarnNoRoute); + SaveIntValue(group, "Localtime", Localtime); + SaveIntValue(group, "SendPtoMultiple", SendPtoMultiple); + + SaveMultiStringValue(group, "FWDAliases", AliasText); + + bbs = config_setting_add(root, "BBSForwarding", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + ForwardingInfo = user->ForwardingInfo; + + if (ForwardingInfo == NULL) + continue; + + if (memcmp(ForwardingInfo, &DummyForwardingInfo, sizeof(struct BBSForwardingInfo)) == 0) + continue; // Ignore empty records; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + char Key[20] = "*"; + strcat (Key, user->Call); + group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + + SaveMultiStringValue(group, "TOCalls", ForwardingInfo->TOCalls); + SaveMultiStringValue(group, "ConnectScript", ForwardingInfo->ConnectScript); + SaveMultiStringValue(group, "ATCalls", ForwardingInfo->ATCalls); + SaveMultiStringValue(group, "HRoutes", ForwardingInfo->Haddresses); + SaveMultiStringValue(group, "HRoutesP", ForwardingInfo->HaddressesP); + SaveMultiStringValue(group, "FWDTimes", ForwardingInfo->FWDTimes); + + SaveIntValue(group, "Enabled", ForwardingInfo->Enabled); + SaveIntValue(group, "RequestReverse", ForwardingInfo->ReverseFlag); + SaveIntValue(group, "AllowBlocked", ForwardingInfo->AllowBlocked); + SaveIntValue(group, "AllowCompressed", ForwardingInfo->AllowCompressed); + SaveIntValue(group, "UseB1Protocol", ForwardingInfo->AllowB1); + SaveIntValue(group, "UseB2Protocol", ForwardingInfo->AllowB2); + SaveIntValue(group, "SendCTRLZ", ForwardingInfo->SendCTRLZ); + + SaveIntValue(group, "FWDPersonalsOnly", ForwardingInfo->PersonalOnly); + SaveIntValue(group, "FWDNewImmediately", ForwardingInfo->SendNew); + SaveIntValue(group, "FwdInterval", ForwardingInfo->FwdInterval); + SaveIntValue(group, "RevFWDInterval", ForwardingInfo->RevFwdInterval); + SaveIntValue(group, "MaxFBBBlock", ForwardingInfo->MaxFBBBlockSize); + SaveIntValue(group, "ConTimeout", ForwardingInfo->ConTimeout); + + SaveStringValue(group, "BBSHA", ForwardingInfo->BBSHA); + } + + + // Save Housekeeping config + + group = config_setting_add(root, "Housekeeping", CONFIG_TYPE_GROUP); + + SaveInt64Value(group, "LastHouseKeepingTime", LastHouseKeepingTime); + SaveInt64Value(group, "LastTrafficTime", LastTrafficTime); + SaveIntValue(group, "MaxMsgno", MaxMsgno); + SaveIntValue(group, "BidLifetime", BidLifetime); + SaveIntValue(group, "MaxAge", MaxAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "MaintInterval", MaintInterval); + SaveIntValue(group, "UserLifetime", UserLifetime); + SaveIntValue(group, "MaintTime", MaintTime); + SaveFloatValue(group, "PR", PR); + SaveFloatValue(group, "PUR", PUR); + SaveFloatValue(group, "PF", PF); + SaveFloatValue(group, "PNF", PNF); + SaveIntValue(group, "BF", BF); + SaveIntValue(group, "BNF", BNF); + SaveIntValue(group, "NTSD", NTSD); + SaveIntValue(group, "NTSF", NTSF); + SaveIntValue(group, "NTSU", NTSU); +// SaveIntValue(group, "AP", AP); +// SaveIntValue(group, "AB", AB); + SaveIntValue(group, "DeletetoRecycleBin", DeletetoRecycleBin); + SaveIntValue(group, "SuppressMaintEmail", SuppressMaintEmail); + SaveIntValue(group, "MaintSaveReg", SaveRegDuringMaint); + SaveIntValue(group, "OverrideUnsent", OverrideUnsent); + SaveIntValue(group, "SendNonDeliveryMsgs", SendNonDeliveryMsgs); + SaveIntValue(group, "GenerateTrafficReport", GenerateTrafficReport); + + SaveOverride(group, "LTFROM", LTFROM); + SaveOverride(group, "LTTO", LTTO); + SaveOverride(group, "LTAT", LTAT); + + // Save UI config + + for (i=1; i<=32; i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + if (group) + { + SaveIntValue(group, "Enabled", UIEnabled[i]); + SaveIntValue(group, "SendMF", UIMF[i]); + SaveIntValue(group, "SendHDDR", UIHDDR[i]); + SaveIntValue(group, "SendNull", UINull[i]); + + if (UIDigi[i]) + SaveStringValue(group, "Digis", UIDigi[i]); + } + } + + // Save User Config + + bbs = config_setting_add(root, "BBSUsers", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + char stats[256], stats2[256]; + struct MsgStats * Stats; + char Key[20] = "*"; + + user = UserRecPtr[i]; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + strcat (Key, user->Call); +// group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + { + strcpy(Key, user->Call); +// group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + } + /* + SaveStringValue(group, "Name", user->Name); + SaveStringValue(group, "Address", user->Address); + SaveStringValue(group, "HomeBBS", user->HomeBBS); + SaveStringValue(group, "QRA", user->QRA); + SaveStringValue(group, "pass", user->pass); + SaveStringValue(group, "ZIP", user->ZIP); + SaveStringValue(group, "CMSPass", user->CMSPass); + + SaveIntValue(group, "lastmsg", user->lastmsg); + SaveIntValue(group, "flags", user->flags); + SaveIntValue(group, "PageLen", user->PageLen); + SaveIntValue(group, "BBSNumber", user->BBSNumber); + SaveIntValue(group, "RMSSSIDBits", user->RMSSSIDBits); + SaveIntValue(group, "WebSeqNo", user->WebSeqNo); + + SaveInt64Value(group, "TimeLastConnected", user->TimeLastConnected); +*/ + Stats = &user->Total; + +// sprintf(stats, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + sprintf(stats, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Totsl", stats); + + Stats = &user->Last; + + sprintf(stats2, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", +// sprintf(stats2, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Last", stats2); + + sprintf(Line,"%s^%s^%s^%s^%s^%s^%s^%d^%d^%d^%d^%d^%d^%lld^%s^%s", + user->Name, user->Address, user->HomeBBS, user->QRA, user->pass, user->ZIP, user->CMSPass, + user->lastmsg, user->flags, user->PageLen, user->BBSNumber, user->RMSSSIDBits, user->WebSeqNo, + user->TimeLastConnected, stats, stats2); + + if (strlen(Line) < 10) + continue; + + SaveStringValue(bbs, Key, Line); + } + +/* + wp = config_setting_add(root, "WP", CONFIG_TYPE_GROUP); + + for (i = 0; i <= NumberofWPrecs; i++) + { + char WPString[1024]; + long long val1, val2; + + WP = WPRecPtr[i]; + val1 = WP->last_modif; + val2 = WP->last_seen; + + sprintf(Key, "R%d", i); + + sprintf(WPString, "%s|%s|%d|%d|%d|%s|%s|%s|%s|%s|%s|%ld|%ld", + &WP->callsign[0], &WP->name[0], WP->Type, WP->changed, WP->seen, &WP->first_homebbs[0], + &WP->secnd_homebbs[0], &WP->first_zip[0], &WP->secnd_zip[0], &WP->first_qth[0], &WP->secnd_qth[0], + val1, val2); + + SaveStringValue(wp, Key, WPString); + } + + // Save Message Headers + + msgs = config_setting_add(root, "MSGS", CONFIG_TYPE_GROUP); + + memset(MsgHddrPtr[0], 0, sizeof(struct MsgInfo)); + + MsgHddrPtr[0]->type = 'X'; + MsgHddrPtr[0]->status = '2'; + MsgHddrPtr[0]->number = 0; + MsgHddrPtr[0]->length = LatestMsg; + + + for (i = 0; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d", Msg->number); + + n = sprintf(Line, "%c|%c|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + + SaveStringValue(msgs, Key, Line); + } + + // Save Bids + + msgs = config_setting_add(root, "BIDS", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofBIDs; i++) + { + sprintf(Key, "R%s", BIDRecPtr[i]->BID); + sprintf(Line, "%d|%d", BIDRecPtr[i]->mode, BIDRecPtr[i]->u.timestamp); + SaveStringValue(msgs, Key, Line); + } + +#ifdef LINBPQ + + if(! config_write_file(&cfg,"/dev/shm/linmail.cfg.temp" )) + { + print("Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + CopyFile("/dev/shm/linmail.cfg.temp", ConfigName, FALSE); + +#else +*/ + if(! config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + +//#endif + + config_destroy(&cfg); + +/* + +#ifndef LINBPQ + + // Save a copy with current Date/Time Stamp for debugging + + { + char Backup[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(Backup,"%s.%02d%02d%02d%02d%02d.save", ConfigName, tm->tm_year-100, tm->tm_mon+1, + tm->tm_mday, tm->tm_hour, tm->tm_min); + + CopyFile(ConfigName, Backup, FALSE); // Copy to .bak + } +#endif +*/ +} + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +long long GetInt64Value(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int64 (setting); + + return 0; +} + +double GetFloatValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + + if (setting) + { + return config_setting_get_float (setting); + } + return 0; +} + +int GetIntValueWithDefault(config_setting_t * group, char * name, int Default) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return Default; +} + + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + value[0] = 0; + return FALSE; +} + +BOOL GetConfig(char * ConfigName) +{ + int i; + char Size[80]; + config_setting_t *setting; + const char * ptr; + + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + char Msg[256]; + sprintf(Msg, "Config File Line %d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); +#ifdef WIN32 + MessageBox(NULL, Msg, "BPQMail", MB_ICONSTOP); +#else + printf("%s", Msg); +#endif + config_destroy(&cfg); + return(EXIT_FAILURE); + } + +#if LIBCONFIG_VER_MINOR > 5 + config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); +#else + config_set_auto_convert (&cfg, 1); +#endif + + group = config_lookup (&cfg, "main"); + + if (group == NULL) + return EXIT_FAILURE; + + SMTPInPort = GetIntValue(group, "SMTPPort"); + POP3InPort = GetIntValue(group, "POP3Port"); + NNTPInPort = GetIntValue(group, "NNTPPort"); + RemoteEmail = GetIntValue(group, "RemoteEmail"); + MaxStreams = GetIntValue(group, "Streams"); + BBSApplNum = GetIntValue(group, "BBSApplNum"); + EnableUI = GetIntValue(group, "EnableUI"); + MailForInterval = GetIntValue(group, "MailForInterval"); + RefuseBulls = GetIntValue(group, "RefuseBulls"); + OnlyKnown = GetIntValue(group, "OnlyKnown"); + SendSYStoSYSOPCall = GetIntValue(group, "SendSYStoSYSOPCall"); + SendBBStoSYSOPCall = GetIntValue(group, "SendBBStoSYSOPCall"); + DontHoldNewUsers = GetIntValue(group, "DontHoldNewUsers"); + DefaultNoWINLINK = GetIntValue(group, "DefaultNoWINLINK"); + ForwardToMe = GetIntValue(group, "ForwardToMe"); + AllowAnon = GetIntValue(group, "AllowAnon"); + UserCantKillT = GetIntValue(group, "UserCantKillT"); + + DontNeedHomeBBS = GetIntValue(group, "DontNeedHomeBBS"); + DontCheckFromCall = GetIntValue(group, "DontCheckFromCall"); + MaxTXSize = GetIntValue(group, "MaxTXSize"); + MaxRXSize = GetIntValue(group, "MaxRXSize"); + ReaddressLocal = GetIntValue(group, "ReaddressLocal"); + ReaddressReceived = GetIntValue(group, "ReaddressReceived"); + WarnNoRoute = GetIntValue(group, "WarnNoRoute"); + SendPtoMultiple = GetIntValue(group, "SendPtoMultiple"); + Localtime = GetIntValue(group, "Localtime"); + AliasText = GetMultiStringValue(group, "FWDAliases"); + GetStringValue(group, "BBSName", BBSName); + GetStringValue(group, "MailForText", MailForText); + GetStringValue(group, "SYSOPCall", SYSOPCall); + GetStringValue(group, "H-Route", HRoute); + GetStringValue(group, "AMPRDomain", AMPRDomain); + SendAMPRDirect = GetIntValue(group, "SendAMPRDirect"); + ISP_Gateway_Enabled = GetIntValue(group, "SMTPGatewayEnabled"); + ISPPOP3Interval = GetIntValue(group, "POP3PollingInterval"); + GetStringValue(group, "MyDomain", MyDomain); + GetStringValue(group, "ISPSMTPName", ISPSMTPName); + GetStringValue(group, "ISPPOP3Name", ISPPOP3Name); + ISPSMTPPort = GetIntValue(group, "ISPSMTPPort"); + ISPPOP3Port = GetIntValue(group, "ISPPOP3Port"); + GetStringValue(group, "ISPAccountName", ISPAccountName); + GetStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); + GetStringValue(group, "ISPAccountName", ISPAccountName); + + sprintf(SignoffMsg, "73 de %s\r", BBSName); // Default + GetStringValue(group, "SignoffMsg", SignoffMsg); + + DecryptPass(EncryptedISPAccountPass, ISPAccountPass, (int)strlen(EncryptedISPAccountPass)); + + SMTPAuthNeeded = GetIntValue(group, "AuthenticateSMTP"); + LogBBS = GetIntValue(group, "Log_BBS"); + LogTCP = GetIntValue(group, "Log_TCP"); + + MulticastRX = GetIntValue(group, "MulticastRX"); + +#ifndef LINBPQ + + GetStringValue(group, "MonitorSize", Size); + sscanf(Size,"%d,%d,%d,%d,%d",&MonitorRect.left,&MonitorRect.right,&MonitorRect.top,&MonitorRect.bottom,&OpenMon); + + GetStringValue(group, "WindowSize", Size); + sscanf(Size,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); + + Bells = GetIntValue(group, "Bells"); + + FlashOnBell = GetIntValue(group, "FlashOnBell"); + + StripLF = GetIntValue(group, "StripLF"); + CloseWindowOnBye = GetIntValue(group, "CloseWindowOnBye"); + WarnWrap = GetIntValue(group, "WarnWrap"); + WrapInput = GetIntValue(group, "WrapInput"); + FlashOnConnect = GetIntValue(group, "FlashOnConnect"); + + GetStringValue(group, "ConsoleSize", Size); + sscanf(Size,"%d,%d,%d,%d,%d", &ConsoleRect.left, &ConsoleRect.right, + &ConsoleRect.top, &ConsoleRect.bottom,&OpenConsole); + +#endif + + // Get Welcome Messages + + setting = config_setting_get_member (group, "WelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + WelcomeMsg = _strdup(ptr); + } + else + WelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "NewUserWelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + NewWelcomeMsg = _strdup(ptr); + } + else + NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "ExpertWelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + ExpertWelcomeMsg = _strdup(ptr); + } + else + ExpertWelcomeMsg = _strdup(""); + + // Get Prompts + + setting = config_setting_get_member (group, "Prompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + Prompt = _strdup(ptr); + } + else + { + Prompt = malloc(20); + sprintf(Prompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "NewUserPrompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + NewPrompt = _strdup(ptr); + } + else + { + NewPrompt = malloc(20); + sprintf(NewPrompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "ExpertPrompt"); + + if (setting && setting->value.sval[0]) + { + ptr = config_setting_get_string (setting); + ExpertPrompt = _strdup(ptr); + } + else + { + ExpertPrompt = malloc(20); + sprintf(ExpertPrompt, "de %s>\r\n", BBSName); + } + + TidyPrompts(); + + RejFrom = GetMultiStringValue(group, "RejFrom"); + RejTo = GetMultiStringValue(group, "RejTo"); + RejAt = GetMultiStringValue(group, "RejAt"); + RejBID = GetMultiStringValue(group, "RejBID"); + + HoldFrom = GetMultiStringValue(group, "HoldFrom"); + HoldTo = GetMultiStringValue(group, "HoldTo"); + HoldAt = GetMultiStringValue(group, "HoldAt"); + HoldBID = GetMultiStringValue(group, "HoldBID"); + + // Send WP Params + + SendWP = GetIntValue(group, "SendWP"); + SendWPType = GetIntValue(group, "SendWPType"); + + GetStringValue(group, "SendWPTO", SendWPTO); + GetStringValue(group, "SendWPVIA", SendWPVIA); + + SendWPAddrs = GetMultiStringValue(group, "SendWPAddrs"); + + FilterWPBulls = GetIntValue(group, "FilterWPBulls"); + NoWPGuesses = GetIntValue(group, "NoWPGuesses"); + + if (SendWPAddrs[0] == NULL && SendWPTO[0]) + { + // convert old format TO and VIA to entry in SendWPAddrs + + SendWPAddrs = realloc(SendWPAddrs, 8); // Add entry + + if (SendWPVIA[0]) + { + char WP[256]; + + sprintf(WP, "%s@%s", SendWPTO, SendWPVIA); + SendWPAddrs[0] = _strdup(WP); + } + else + SendWPAddrs[0] = _strdup(SendWPTO); + + + SendWPAddrs[1] = 0; + + SendWPTO[0] = 0; + SendWPVIA[0] = 0; + } + + GetStringValue(group, "Version", Size); + sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); + + for (i=1; i<=32; i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_lookup (&cfg, Key); + + if (group) + { + UIEnabled[i] = GetIntValue(group, "Enabled"); + UIMF[i] = GetIntValueWithDefault(group, "SendMF", UIEnabled[i]); + UIHDDR[i] = GetIntValueWithDefault(group, "SendHDDR", UIEnabled[i]); + UINull[i] = GetIntValue(group, "SendNull"); + Size[0] = 0; + GetStringValue(group, "Digis", Size); + if (Size[0]) + UIDigi[i] = _strdup(Size); + } + } + + group = config_lookup (&cfg, "Housekeeping"); + + if (group) + { + LastHouseKeepingTime = GetIntValue(group, "LastHouseKeepingTime"); + LastTrafficTime = GetIntValue(group, "LastTrafficTime"); + MaxMsgno = GetIntValue(group, "MaxMsgno"); + LogAge = GetIntValue(group, "LogLifetime"); + BidLifetime = GetIntValue(group, "BidLifetime"); + MaxAge = GetIntValue(group, "MaxAge"); + if (MaxAge == 0) + MaxAge = 30; + UserLifetime = GetIntValue(group, "UserLifetime"); + MaintInterval = GetIntValue(group, "MaintInterval"); + + if (MaintInterval == 0) + MaintInterval = 24; + + MaintTime = GetIntValue(group, "MaintTime"); + + PR = GetFloatValue(group, "PR"); + PUR = GetFloatValue(group, "PUR"); + PF = GetFloatValue(group, "PF"); + PNF = GetFloatValue(group, "PNF"); + + BF = GetIntValue(group, "BF"); + BNF = GetIntValue(group, "BNF"); + NTSD = GetIntValue(group, "NTSD"); + NTSU = GetIntValue(group, "NTSU"); + NTSF = GetIntValue(group, "NTSF"); +// AP = GetIntValue(group, "AP"); +// AB = GetIntValue(group, "AB"); + DeletetoRecycleBin = GetIntValue(group, "DeletetoRecycleBin"); + SuppressMaintEmail = GetIntValue(group, "SuppressMaintEmail"); + SaveRegDuringMaint = GetIntValue(group, "MaintSaveReg"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + SendNonDeliveryMsgs = GetIntValue(group, "SendNonDeliveryMsgs"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + GenerateTrafficReport = GetIntValueWithDefault(group, "GenerateTrafficReport", 1); + + LTFROM = GetOverrides(group, "LTFROM"); + LTTO = GetOverrides(group, "LTTO"); + LTAT = GetOverrides(group, "LTAT"); + } + + return EXIT_SUCCESS; +} + + +int Connected(int Stream) +{ + int n, Mask; + CIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + char Title[100]; + int Freq = 0; + int Mode = 0; + BPQVECSTRUC * SESS; + TRANSPORTENTRY * Sess1 = NULL, * Sess2; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + if (conn->BBSFlags & RunningConnectScript) + { + // BBS Outgoing Connect + + conn->paclen = 236; + + // Run first line of connect script + + ChangeSessionIdletime(Stream, BBSIDLETIME); // Default Idletime for BBS Sessions + ProcessBBSConnectScript(conn, ConnectedMsg, 15); + return 0; + } + } + + // Incoming Connect + + // Try to find port, freq, mode, etc + +#ifdef LINBPQ + SESS = &BPQHOSTVECTOR[0]; +#else + SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + SESS +=(Stream - 1); + + if (SESS) + Sess1 = SESS->HOSTSESSION; + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // See if L2 session - if so, get info from WL2K report line + + // if Session has report info, use it + + if (Sess2->Mode) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + else if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + Freq = PORT->WL2KInfo.Freq; + Mode = PORT->WL2KInfo.mode; + } + else + { + if (Sess2->RMSCall[0]) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + } + } + } + + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + strlop(callsign, ' '); // Remove trailing spaces + + if (strcmp(&callsign[strlen(callsign) - 2], "-T") == 0) + conn->RadioOnlyMode = 'T'; + else if (strcmp(&callsign[strlen(callsign) - 2], "-R") == 0) + conn->RadioOnlyMode = 'R'; + else + conn->RadioOnlyMode = 0; + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = LookupCall(callsign); + + if (user == NULL) + { + int Length=0; + + if (OnlyKnown) + { + // Unknown users not allowed + + n = sprintf_s(Msg, sizeof(Msg), "Incoming Connect from unknown user %s Rejected", callsign); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + Disconnect(Stream); + return 0; + } + + user = AllocateUserRecord(callsign); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (SendNewUserMessage) + { + char * MailBuffer = malloc(100); + Length += sprintf(MailBuffer, "New User %s Connected to Mailbox on Port %d Freq %d Mode %d\r\n", callsign, port, Freq, Mode); + + sprintf(Title, "New User %s", callsign); + + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + if (user == NULL) return 0; // Cant happen?? + + if (!DontHoldNewUsers) + user->flags |= F_HOLDMAIL; + + if (DefaultNoWINLINK) + user->flags |= F_NOWINLINK; + + // Always set WLE User - can't see it doing any harm + + user->flags |= F_Temp_B2_BBS; + + conn->NewUser = TRUE; + } + + user->TimeLastConnected = time(NULL); + user->Total.ConnectsIn++; + + conn->UserPointer = user; + + conn->lastmsg = user->lastmsg; + + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (paclen == 0) + { + paclen = 236; + + if (conn->SessType & Sess_PACTOR) + paclen = 100; + } + + conn->paclen = paclen; + + // Set SYSOP flag if user is defined as SYSOP and Host Session + + if (((conn->SessType & Sess_BPQHOST) == Sess_BPQHOST) && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + if (conn->Secure_Session && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + Mask = 1 << (GetApplNum(Stream) - 1); + + if (user->flags & F_Excluded) + { + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s Rejected by Exclude Flag", user->Call); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + Disconnect(Stream); + return 0; + } + + if (port) + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s on Port %d Freq %d Mode %s", + user->Call, port, Freq, WL2KModes[Mode]); + else + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt (Unless Sync) + + if (user->ForwardingInfo && user->ForwardingInfo->ConTimeout) + conn->SIDResponseTimer = user->ForwardingInfo->ConTimeout / 10; // 10 sec ticks + else + conn->SIDResponseTimer = 12; // Allow a couple of minutes for response to SID + + { + BOOL B1 = FALSE, B2 = FALSE, BIN = FALSE, BLOCKED = FALSE; + BOOL WL2KRO = FALSE; + + struct BBSForwardingInfo * ForwardingInfo; + + if (conn->RadioOnlyMode == 'R') + WL2KRO = 1; + + conn->PageLen = user->PageLen; + conn->Paging = (user->PageLen > 0); + + if (user->flags & F_Temp_B2_BBS) + { + // An RMS Express user that needs a temporary BBS struct + + if (user->ForwardingInfo == NULL) + { + // we now save the Forwarding info if BBS flag is cleared, + // so there may already be a ForwardingInfo + + user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + } + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; + + ForwardingInfo = user->ForwardingInfo; + + ForwardingInfo->AllowCompressed = TRUE; + B1 = ForwardingInfo->AllowB1 = FALSE; + B2 = ForwardingInfo->AllowB2 = TRUE; + BLOCKED = ForwardingInfo->AllowBlocked = TRUE; + } + + if (conn->NewUser) + { + BLOCKED = TRUE; + BIN = TRUE; + B2 = TRUE; + } + + if (user->ForwardingInfo) + { + BLOCKED = user->ForwardingInfo->AllowBlocked; + if (BLOCKED) + { + BIN = user->ForwardingInfo->AllowCompressed; + B1 = user->ForwardingInfo->AllowB1; + B2 = user->ForwardingInfo->AllowB2; + } + } + + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + if (conn->RadioOnlyMode) + nodeprintf(conn,";WL2K-Radio/Internet_Network\r"); + + if (!(conn->BBSFlags & SYNCMODE)) + { + + nodeprintf(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", + BLOCKED ? "FW": "", WL2KRO ? "" : "J"); + + // if (user->flags & F_Temp_B2_BBS) + // nodeprintf(conn,";PQ: 66427529\r"); + + // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); + } + } + + if ((user->Name[0] == 0) & AllowAnon) + strcpy(user->Name, user->Call); + + if (!(conn->BBSFlags & SYNCMODE)) + { + if (user->Name[0] == 0) + { + conn->Flags |= GETTINGUSER; + BBSputs(conn, NewUserPrompt); + } + else + SendWelcomeMsg(Stream, conn, user); + } + else + { + // Seems to be a timing problem - see if this fixes it + + Sleep(500); + } + + RefreshMainWindow(); + + return 0; + } + } + + return 0; +} + +int Disconnected (int Stream) +{ + struct UserInfo * user = NULL; + CIRCUIT * conn; + int n; + char Msg[255]; + int len; + char DiscMsg[] = "DISCONNECTED "; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active == FALSE) + return 0; + + // if still running connect script, reenter it to see if + // there is an else + + if (conn->BBSFlags & RunningConnectScript) + { + // We need to see if we got as far as connnected, + // as if we have we need to reset the connect script + // over the ELSE + + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + // First see if any script left + + if (Scripts[ForwardingInfo->ScriptIndex]) + { + if (ForwardingInfo->MoreLines == FALSE) + { + // Have reached end of script, so need to set back over ELSE + + ForwardingInfo->ScriptIndex--; + ForwardingInfo->MoreLines = TRUE; + } + + // if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + + + ProcessBBSConnectScript(conn, DiscMsg, 15); + return 0; + } + } + + // if sysop was chatting to user clear link +#ifndef LINBPQ + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, "User has disconnected\n", 23); + BBSConsole.Console->SysopChatStream = 0; + } +#endif + ClearQueue(conn); + + if (conn->PacLinkCalls) + free(conn->PacLinkCalls); + + if (conn->InputBuffer) + { + free(conn->InputBuffer); + conn->InputBuffer = NULL; + conn->InputBufferLen = 0; + } + + if (conn->InputMode == 'B') + { + // Save partly received message for a restart + + if (conn->BBSFlags & FBBB1Mode) + if (conn->Paclink == 0) // Paclink doesn't do restarts + if (strcmp(conn->Callsign, "RMS") != 0) // Neither does RMS Packet. + if (conn->DontSaveRestartData == FALSE) + SaveFBBBinary(conn); + } + + conn->Active = FALSE; + + if (conn->FwdMsg) + conn->FwdMsg->Locked = 0; // Unlock + + RefreshMainWindow(); + + RemoveTempBIDS(conn); + + len=sprintf_s(Msg, sizeof(Msg), "%s Disconnected", conn->Callsign); + WriteLogLine(conn, '|',Msg, len, LOG_BBS); + + if (conn->FBBHeaders) + { + struct FBBHeaderLine * FBBHeader; + int n; + + for (n = 0; n < 5; n++) + { + FBBHeader = &conn->FBBHeaders[n]; + + if (FBBHeader->FwdMsg) + FBBHeader->FwdMsg->Locked = 0; // Unlock + + } + + free(conn->FBBHeaders); + conn->FBBHeaders = NULL; + } + + if (conn->UserPointer) + { + struct BBSForwardingInfo * FWDInfo = conn->UserPointer->ForwardingInfo; + + if (FWDInfo) + { + FWDInfo->Forwarding = FALSE; + +// if (FWDInfo->UserCall[0]) // Will be set if RMS +// { +// FindNextRMSUser(FWDInfo); +// } +// else + FWDInfo->FwdTimer = 0; + } + } + + conn->BBSFlags = 0; // Clear ARQ Mode + + return 0; + } + } + return 0; +} + +int DoReceivedData(int Stream) +{ + int count, InputLen; + size_t MsgLen; + int n; + CIRCUIT * conn; + struct UserInfo * user; + char * ptr, * ptr2; + char * Buffer; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + conn->SIDResponseTimer = 0; // Got a message, so cancel timeout. + + do + { + // May have several messages per packet, or message split over packets + + OuterLoop: + if (conn->InputLen + 1000 > conn->InputBufferLen ) // Shouldnt have lines longer than this in text mode + { + conn->InputBufferLen += 1000; + conn->InputBuffer = realloc(conn->InputBuffer, conn->InputBufferLen); + } + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0 && conn->InputMode != 'Y') + return 0; + + conn->InputLen += InputLen; + + if (conn->InputLen == 0) return 0; + + conn->Watchdog = 900; // 15 Minutes + + if (conn->InputMode == 'Y') // YAPP + { + if (ProcessYAPPMessage(conn)) // Returns TRUE if there could be more to process + goto OuterLoop; + + return 0; + } + + if (conn->InputMode == 'B') + { + // if in OpenBCM mode, remove FF transparency + + if (conn->OpenBCM) // Telnet, so escape any 0xFF + { + unsigned char * ptr1 = conn->InputBuffer; + unsigned char * ptr2; + int Len; + unsigned char c; + + // We can come through here again for the + // same data as we wait for a full packet + // So only check last InputLen bytes + + ptr1 += (conn->InputLen - InputLen); + ptr2 = ptr1; + Len = InputLen; + + while (Len--) + { + c = *(ptr1++); + + if (conn->InTelnetExcape) // Last char was ff + { + conn->InTelnetExcape = FALSE; + continue; + } + + *(ptr2++) = c; + + if (c == 0xff) // + conn->InTelnetExcape = TRUE; + } + + conn->InputLen = (int)(ptr2 - conn->InputBuffer); + } + + UnpackFBBBinary(conn); + goto OuterLoop; + } + else + { + + loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + return 0; + } + + user = conn->UserPointer; + + if (conn->BBSFlags & (MCASTRX | SYNCMODE)) + { + // MCAST and SYNCMODE deliver full packets + + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + + conn->InputLen=0; + continue; + } + + // This looks for CR, CRLF, LF or CR/Null and removes any LF or NULL, + // but this relies on both arriving in same packet. + // Need to check for LF and start of packet and ignore it + // But what if client is only using LF?? + // (WLE sends SID with CRLF, other packets with CR only) + + // We don't get here on the data part of a binary transfer, so + // don't need to worry about messing up binary data. + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + ptr2 = memchr(conn->InputBuffer, '\n', conn->InputLen); + + if (ptr) + conn->usingCR = 1; + + if ((ptr2 && ptr2 < ptr) || ptr == 0) // LF before CR, or no CR + ptr = ptr2; // Use LF + + if (ptr) // CR or LF in buffer + { + conn->lastLineEnd = *(ptr); + + *(ptr) = '\r'; // In case was LF + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single msg in buffer + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (conn->InputLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + } + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (ptr2-ptr); + + Buffer = malloc(MsgLen + 100); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (MsgLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, Buffer, (int)MsgLen); + else + ProcessLine(conn, user, Buffer, (int)MsgLen); + } + free(Buffer); + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + + conn->InputLen -= (int)MsgLen; + + goto loop; + + } + } + else + { + // Could be a YAPP Header + + + if (conn->InputLen == 2 && conn->InputBuffer[0] == ENQ && conn->InputBuffer[1] == 1) // YAPP Send_Init + { + UCHAR YAPPRR[2]; + YAPPRR[0] = ACK; + YAPPRR[1] = 1; + + conn->InputMode = 'Y'; + QueueMsg(conn, YAPPRR, 2); + + conn->InputLen = 0; + return 0; + } + } + } + + } while (count > 0); + + return 0; + } + } + + // Socket not found + + return 0; + +} +int DoBBSMonitorData(int Stream) +{ +// UCHAR Buffer[1000]; + UCHAR buff[500]; + + int len = 0,count=0; + int stamp; + + do + { + stamp=GetRaw(Stream, buff,&len,&count); + + if (len == 0) return 0; + + SeeifBBSUIFrame((struct _MESSAGEX *)buff, len); + } + + while (count > 0); + + + return 0; + +} + +VOID ProcessFLARQLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) +{ + Buffer[MsgLen] = 0; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + + if (strcmp(Buffer, "ARQ::ETX\r") == 0) + { + // Decode it. + + UCHAR * ptr1, * ptr2, * ptr3; + int len, linelen; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + int LocalMsg[1000] ; // Set if Recipient is a local wl2k address + + int B2To; // Offset to To: fields in B2 header + int Recipients = 0; + int RMSMsgs = 0, BBSMsgs = 0; + +// Msg->B2Flags |= B2Msg; + + + ptr1 = conn->MailBuffer; + len = Msg->length; + ptr1[len] = 0; + + if (strstr(ptr1, "ARQ:ENCODING::")) + { + // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now + + _strupr(conn->MailBuffer); + if (strstr(conn->MailBuffer, "BBSPOLL")) + { + SendARQMail(conn); + } + + free(conn->MailBuffer); + conn->MailBuffer = NULL; + conn->MailBufferSize = 0; + + return; + } + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: + { + char SaveFrom[100]; + char * FromHA; + + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + + _strupr(FullTo); + + B2To = (int)(ptr1 - conn->MailBuffer); + + if (_memicmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + strcpy(FullTo, "RMS"); + strcpy(Msg->via, &FullTo[4]); + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + size_t Subjlen = ptr2 - &ptr1[9]; + if (Subjlen > 60) Subjlen = 60; + memcpy(Msg->title, &ptr1[9], Subjlen); + + goto ProcessBody; + } +// else if (_memicmp(ptr1, "Body:", 4) == 0) +// { +// MsgLen = atoi(&ptr1[5]); +// StartofMsg = ptr1; +// } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", + &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + Msg->datecreated = Date; + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip cr + goto Loop; + } + + + // Processed all headers +ProcessBody: + + ptr2 +=2; // skip crlf + + Msg->length = (int)(&conn->MailBuffer[Msg->length] - ptr2); + + memmove(conn->MailBuffer, ptr2, Msg->length); + + CreateMessageFromBuffer(conn); + + conn->BBSFlags = 0; // Clear ARQ Mode + return; + } + + // File away the data + + Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); + + conn->TempMsg->length += MsgLen; + + return; + + // Not sure what to do yet with files, but will process emails (using text style forwarding + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + + return; +} + +VOID ProcessTextFwdLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int len) +{ + Buffer[len] = 0; +// Debugprintf(Buffer); + + // With TNC2 body prompt is a single CR, so that shouldn't be ignored. + + // If thia causes problems with other TNC PMS implementations I'll have to revisit this + +// if (len == 1 && Buffer[0] == 13) +// return; + + if (conn->Flags & SENDTITLE) + { + // Waiting for Subject: prompt + + struct MsgInfo * Msg = conn->FwdMsg; + + nodeprintf(conn, "%s\r", Msg->title); + + conn->Flags &= ~SENDTITLE; + conn->Flags |= SENDBODY; + + // New Paccom PMS (V3.2) doesn't prompt for body so drop through and send it + if ((conn->BBSFlags & NEWPACCOM) == 0) + return; + + } + + if (conn->Flags & SENDBODY) + { + // Waiting for Enter Message Prompt + + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + char * MsgPtr; + int MsgLen; + int Index = 0; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &conn->FwdMsg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + BBSputs(conn, "\r"); + + MsgLen = RemoveLF(MsgPtr, MsgLen); + + QueueMsg(conn, MsgPtr, MsgLen); + + if (user->ForwardingInfo->SendCTRLZ) + nodeprintf(conn, "\r\x1a"); + else + nodeprintf(conn, "\r/ex\r"); + + free(MsgBytes); + + conn->FBBMsgsSent = TRUE; + + + if (conn->FwdMsg->type == 'P') + Index = PMSG; + else if (conn->FwdMsg->type == 'B') + Index = BMSG; + else if (conn->FwdMsg->type == 'T') + Index = TMSG; + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + conn->Flags &= ~SENDBODY; + conn->Flags |= WAITPROMPT; + + return; + } + + if (conn->Flags & WAITPROMPT) + { + if (Buffer[len-2] != '>') + return; + + conn->Flags &= ~WAITPROMPT; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + SaveMessageDatabase(); + + conn->UserPointer->ForwardingInfo->MsgCount--; + + // See if any more to forward + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // If we are using SETCALLTOSENDER make sure this message is from the same sender + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + unsigned char AXCall[7]; + + Msg = conn->FwdMsg; + ConvToAX25(Msg->from, AXCall); + if (memcmp(SESS[conn->BPQStream - 1].HOSTSESSION->L4USER, AXCall, 7) != 0) + { + Disconnect(conn->BPQStream); + return; + } + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + conn->Flags |= SENDTITLE; + + + if ((conn->BBSFlags & SETCALLTOSENDER)) + nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); + else + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + Disconnect(conn->BPQStream); + } + return; + } +} + + +#define N 2048 /* buffer size */ +#define F 60 /* lookahead buffer size */ +#define THRESHOLD 2 +#define NIL N /* leaf of tree */ + +extern UCHAR * infile; + +BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen); + + +VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + char * Cmd, * Arg1; + char * Context; + char seps[] = " \t\r"; + int CmdLen; + + if (_memicmp(Buffer, "POSYNCLOGON", 11) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + Sleep(500); + + BBSputs(conn, "OK\r"); + Flush(conn); + return; + } + + if (_memicmp(Buffer, "POSYNCHELLO", 11) == 0) + { + // This is first message received after connecting to SYNC + // Save Callsign + + char Reply[32]; + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + sprintf(Reply, "POSYNCLOGON %s\r", BBSName); + BBSputs(conn, Reply); + return; + } + + if (conn->BBSFlags & SYNCMODE) + { + ProcessSyncModeMessage(conn, user, Buffer, len); + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // A few messages should be trapped here and result in an immediate disconnect, whatever mode I think the session is in (it could be wrong) + + // *** Protocol Error + // Already Connected + // Invalid Command + + if (_memicmp(Buffer, "Already Connected", 17) == 0 || + _memicmp(Buffer, "Invalid Command", 15) == 0 || + _memicmp(Buffer, "*** Protocol Error", 18) == 0) + { + conn->BBSFlags |= DISCONNECTING; + Disconnect(conn->BPQStream); + return; + } + + if (conn->BBSFlags & FBBForwarding) + { + ProcessFBBLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & FLARQMODE) + { + ProcessFLARQLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MCASTRX) + { + ProcessMCASTLine(conn, user, Buffer, len); + return; + } + + + if (conn->BBSFlags & TEXTFORWARDING) + { + ProcessTextFwdLine(conn, user, Buffer, len); + return; + } + + // if chatting to sysop pass message to BBS console + + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, Buffer,len); + return; + } + + if (conn->Flags & GETTINGMESSAGE) + { + ProcessMsgLine(conn, user, Buffer, len); + return; + } + if (conn->Flags & GETTINGTITLE) + { + ProcessMsgTitle(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MBLFORWARDING) + { + ProcessMBLLine(conn, user, Buffer, len); + return; + } + + if (conn->Flags & GETTINGUSER || conn->NewUser) // Could be new user but dont need name + { + if (memcmp(Buffer, ";FW:", 4) == 0 || Buffer[0] == '[') + { + struct BBSForwardingInfo * ForwardingInfo; + + conn->Flags &= ~GETTINGUSER; + + // New User is a BBS - create a temp struct for it + + if ((user->flags & (F_BBS | F_Temp_B2_BBS)) == 0) // It could already be a BBS without a user name + { + // Not defined as BBS - allocate and initialise forwarding structure + + user->flags |= F_Temp_B2_BBS; + + // An RMS Express user that needs a temporary BBS struct + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + ForwardingInfo->AllowCompressed = TRUE; + ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->ForwardingInfo->AllowB2 = TRUE; + } + SaveUserDatabase(); + } + else + { + if (conn->Flags & GETTINGUSER) + { + conn->Flags &= ~GETTINGUSER; + if (len > 18) + len = 18; + + memcpy(user->Name, Buffer, len-1); + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + UpdateWPWithUserInfo(user); + return; + } + } + } + + // Process Command + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + { + // Waiting for paging prompt + + if (len > 1) + { + if (_memicmp(Buffer, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + } + + conn->LinesSent = 0; + return; + } + + if (user->Temp->ListSuspended) + { + // Paging limit hit when listing. User may abort, continue, or read one or more messages + + ProcessSuspendedListCommand(conn, user, Buffer, len); + return; + } + if (len == 1) + { + SendPrompt(conn, user); + return; + } + + Buffer[len] = 0; + + if (strstr(Buffer, "ARQ:FILE:")) + { + // Message from FLARQ + + conn->BBSFlags |= FLARQMODE; + strcpy(conn->ARQFilename, &Buffer[10]); // Will need name when we decide what to do with files + + // Create a Temp Messge Stucture + + CreateMessage(conn, conn->Callsign, "", "", 'P', NULL, NULL); + + Buffer[len++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + return; + } + if (Buffer[0] == ';') // WL2K Comment + { + if (memcmp(Buffer, ";FW:", 4) == 0) + { + // Paclink User Select (poll for list) + + char * ptr1,* ptr2, * ptr3; + int index=0; + + // Convert string to Multistring + + Buffer[len-1] = 0; + + conn->PacLinkCalls = zalloc(len*3); + + ptr1 = &Buffer[5]; + ptr2 = (char *)conn->PacLinkCalls; + ptr2 += (len * 2); + strcpy(ptr2, ptr1); + + while (ptr2) + { + ptr3 = strlop(ptr2, ' '); + + if (strlen(ptr2)) + conn->PacLinkCalls[index++] = ptr2; + + ptr2 = ptr3; + } + + return; + } + + if (memcmp(Buffer, ";FR:", 4) == 0) + { + // New Message from TriMode - Just igonre till I know what to do with it + + return; + } + + // Ignore other ';' message + + return; + } + + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // If a BBS, set BBS Flag + + if (user->flags & ( F_BBS | F_Temp_B2_BBS)) + { + if (user->ForwardingInfo) + { + if (user->ForwardingInfo->Forwarding && ((conn->BBSFlags & OUTWARDCONNECT) == 0)) + { + BBSputs(conn, "Already Connected\r"); + Flush(conn); + Sleep(500); + Disconnect(conn->BPQStream); + return; + } + } + + if (user->ForwardingInfo) + { + user->ForwardingInfo->Forwarding = TRUE; + user->ForwardingInfo->FwdTimer = 0; // So we dont send to immediately + } + } + + if (user->flags & ( F_BBS | F_PMS | F_Temp_B2_BBS)) + { + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + conn->FBBChecksum = 0; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + } + else + FBBputs(conn, ">\r"); + + } + + return; + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + return; + } + + Arg1 = strtok_s(NULL, seps, &Context); + CmdLen = (int)strlen(Cmd); + + // Check List first. If any other, save last listed to user record. + + if (_memicmp(Cmd, "L", 1) == 0 && _memicmp(Cmd, "LISTFILES", 3) != 0) + { + DoListCommand(conn, user, Cmd, Arg1, FALSE, Context); + SendPrompt(conn, user); + return; + } + + if (conn->lastmsg > user->lastmsg) + { + user->lastmsg = conn->lastmsg; + SaveUserDatabase(); + } + + if (_stricmp(Cmd, "SHOWRMSPOLL") == 0) + { + DoShowRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "AUTH") == 0) + { + DoAuthCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + if (_memicmp(Cmd, "Bye", CmdLen) == 0 || _stricmp(Cmd, "ELSE") == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + Disconnect(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + + if (_memicmp(Cmd, "Node", 4) == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + ReturntoNode(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + + if (_memicmp(Cmd, "IDLETIME", 4) == 0) + { + DoSetIdleTime(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "SETNEXTMESSAGENUMBER") == 0) + { + DoSetMsgNo(conn, user, Arg1, Context); + return; + } + + if (strlen(Cmd) < 12 && _memicmp(Cmd, "D", 1) == 0) + { + DoDeliveredCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "K", 1) == 0) + { + DoKillCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "LISTFILES", 3) == 0 || _memicmp(Cmd, "FILES", 5) == 0) + { + ListFiles(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "READFILE", 4) == 0) + { + ReadBBSFile(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "REROUTEMSGS", 7) == 0) + { + if (conn->sysop == 0) + nodeprintf(conn, "Reroute Messages needs SYSOP status\r"); + else + { + ReRouteMessages(); + nodeprintf(conn, "Ok\r"); + } + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "YAPP", 4) == 0) + { + YAPPSendFile(conn, user, Arg1); + return; + } + + if (_memicmp(Cmd, "UH", 2) == 0 && conn->sysop) + { + DoUnholdCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_stricmp(Cmd, "IMPORT") == 0) + { + DoImportCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "EXPORT") == 0) + { + DoExportCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "I", 1) == 0) + { + char * Save; + char * MsgBytes; + + if (Arg1) + { + // User WP lookup + + DoWPLookup(conn, user, Cmd[1], Arg1); + SendPrompt(conn, user); + return; + } + + + MsgBytes = Save = ReadInfoFile("info.txt"); + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + BBSputs(conn, "SYSOP has not created an INFO file\r"); + + + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "Name", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 17) + Arg1[17] = 0; + + strcpy(user->Name, Arg1); + UpdateWPWithUserInfo(user); + + } + + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "OP", 2) == 0) + { + int Lines; + + // Paging Control. Param is number of lines per page + + if (Arg1) + { + Lines = atoi(Arg1); + + if (Lines) // Sanity Check + { + if (Lines < 10) + { + nodeprintf(conn,"Page Length %d is too short\r", Lines); + SendPrompt(conn, user); + return; + } + } + + user->PageLen = Lines; + conn->PageLen = Lines; + conn->Paging = (Lines > 0); + SaveUserDatabase(); + } + + nodeprintf(conn,"Page Length is %d\r", user->PageLen); + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "QTH", CmdLen) == 0) + { + if (Arg1) + { + // QTH may contain spaces, so put back together, and just split at cr + + Arg1[strlen(Arg1)] = ' '; + strtok_s(Arg1, "\r", &Context); + + if (strlen(Arg1) > 60) + Arg1[60] = 0; + + strcpy(user->Address, Arg1); + UpdateWPWithUserInfo(user); + + } + + nodeprintf(conn,"QTH is %s\r", user->Address); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "ZIP", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 8) + Arg1[8] = 0; + + strcpy(user->ZIP, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + } + + nodeprintf(conn,"ZIP is %s\r", user->ZIP); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "CMSPASS", 7) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 15) + Arg1[15] = 0; + + strcpy(user->CMSPass, Arg1); + nodeprintf(conn,"CMS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "PASS", CmdLen) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 12) + Arg1[12] = 0; + + strcpy(user->pass, Arg1); + nodeprintf(conn,"BBS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + + if (_memicmp(Cmd, "R", 1) == 0) + { + DoReadCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "S", 1) == 0) + { + if (!DoSendCommand(conn, user, Cmd, Arg1, Context)) + SendPrompt(conn, user); + return; + } + + if ((_memicmp(Cmd, "Help", CmdLen) == 0) || (_memicmp(Cmd, "?", 1) == 0)) + { + char * Save; + char * MsgBytes = Save = ReadInfoFile("help.txt"); + + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + { + BBSputs(conn, "A - Abort Output\r"); + BBSputs(conn, "B - Logoff\r"); + BBSputs(conn, "CMSPASS Password - Set CMS Password\r"); + BBSputs(conn, "D - Flag NTS Message(s) as Delivered - D num\r"); + BBSputs(conn, "HOMEBBS - Display or get HomeBBS\r"); + BBSputs(conn, "INFO - Display information about this BBS\r"); + BBSputs(conn, "I CALL - Lookup CALL in WP Allows *CALL CALL* *CALL* wildcards\r"); + BBSputs(conn, "I@ PARAM - Lookup @BBS in WP\r"); + BBSputs(conn, "IZ PARAM - Lookup Zip Codes in WP\r"); + BBSputs(conn, "IH PARAM - Lookup HA elements in WP - eg USA EU etc\r"); + + BBSputs(conn, "K - Kill Message(s) - K num, KM (Kill my read messages)\r"); + BBSputs(conn, "L - List Message(s) - \r"); + BBSputs(conn, " L = List New, LR = List New (Oldest first)\r"); + BBSputs(conn, " LM = List Mine, L> Call, L< Call, L@ = List to, from or at\r"); + BBSputs(conn, " LL num = List msg num, L num-num = List Range\r"); + BBSputs(conn, " LN LY LH LK LF L$ LD = List Message with corresponding Status\r"); + BBSputs(conn, " LB LP LT = List Mesaage with corresponding Type\r"); + BBSputs(conn, " LC = List TO fields of all active bulletins\r"); + BBSputs(conn, " You can combine most selections eg LMP, LMN LB< G8BPQ\r"); + BBSputs(conn, "LISTFILES or FILES - List files available for download\r"); + + BBSputs(conn, "N Name - Set Name\r"); + BBSputs(conn, "NODE - Return to Node\r"); + BBSputs(conn, "OP n - Set Page Length (Output will pause every n lines)\r"); + BBSputs(conn, "PASS Password - Set BBS Password\r"); + BBSputs(conn, "POLLRMS - Manage Polling for messages from RMS \r"); + BBSputs(conn, "Q QTH - Set QTH\r"); + BBSputs(conn, "R - Read Message(s) - R num \r"); + BBSputs(conn, " RM (Read new messages to me), RMR (RM oldest first)\r"); + BBSputs(conn, "READ Name - Read File\r"); + + BBSputs(conn, "S - Send Message - S or SP Send Personal, SB Send Bull, ST Send NTS,\r"); + BBSputs(conn, " SR Num - Send Reply, SC Num - Send Copy\r"); + BBSputs(conn, "X - Toggle Expert Mode\r"); + BBSputs(conn, "YAPP - Download file from BBS using YAPP protocol\r"); + if (conn->sysop) + { + BBSputs(conn, "DOHOUSEKEEPING - Run Housekeeping process\r"); + BBSputs(conn, "EU - Edit User Flags - Type EU for Help\r"); + BBSputs(conn, "EXPORT - Export messages to file - Type EXPORT for Help\r"); + BBSputs(conn, "FWD - Control Forwarding - Type FWD for Help\r"); + BBSputs(conn, "IMPORT - Import messages from file - Type IMPORT for Help\r"); + BBSputs(conn, "REROUTEMSGS - Rerun message routing process\r"); + BBSputs(conn, "SETNEXTMESSAGENUMBER - Sets next message number\r"); + BBSputs(conn, "SHOWRMSPOLL - Displays your RMS polling list\r"); + BBSputs(conn, "UH - Unhold Message(s) - UH ALL or UH num num num...\r"); + } + } + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "Ver", CmdLen) == 0) + { + nodeprintf(conn, "BBS Version %s\rNode Version %s\r", VersionStringWithBuild, GetVersionString()); + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "HOMEBBS", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 40) Arg1[40] = 0; + + strcpy(user->HomeBBS, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + + if (!strchr(Arg1, '.')) + BBSputs(conn, "Please enter HA with HomeBBS eg g8bpq.gbr.eu - this will help message routing\r"); + } + + nodeprintf(conn,"HomeBBS is %s\r", user->HomeBBS); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if ((_memicmp(Cmd, "EDITUSER", 5) == 0) || (_memicmp(Cmd, "EU", 2) == 0)) + { + DoEditUserCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "POLLRMS") == 0) + { + DoPollRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "DOHOUSEKEEPING") == 0) + { + DoHousekeepingCmd(conn, user, Arg1, Context); + return; + } + + + if (_stricmp(Cmd, "FWD") == 0) + { + DoFwdCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "X", 1) == 0) + { + user->flags ^= F_Expert; + + if (user->flags & F_Expert) + BBSputs(conn, "Expert Mode\r"); + else + BBSputs(conn, "Expert Mode off\r"); + + SaveUserDatabase(); + SendPrompt(conn, user); + return; + } + + if (conn->Flags == 0) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + } + + // Send if possible + + Flush(conn); +} + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...) +{ + // seems to be printf to a socket + + char buff[600]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + BBSputs(conn, buff); +} + +// Code to delete obsolete files from Mail folder + +#ifdef WIN32 + +int DeleteRedundantMessages() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + int Msgno; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, MailDir); + strcat(szDir, "\\*.mes"); + + + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + return 0; + } + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + Msgno = atoi(&ffd.cFileName[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "%s/%s%c", MailDir, ffd.cFileName, 0); + Debugprintf("Tidy Mail - Delete %s\n", File); + +// if (DeletetoRecycleBin) + DeletetoRecycle(File); +// else +// DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return 0; +} + +#else + +#include + +int MsgFilter(const struct dirent * dir) +{ + return (strstr(dir->d_name, ".mes") != 0); +} + +int DeleteRedundantMessages() +{ + struct dirent **namelist; + int n; + struct stat STAT; + int Msgno = 0, res; + char File[100]; + + n = scandir("Mail", &namelist, MsgFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + if (stat(namelist[n]->d_name, &STAT) == 0); + { + Msgno = atoi(&namelist[n]->d_name[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "Mail/%s", namelist[n]->d_name); + printf("Deleting %s\n", File); + unlink(File); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + +VOID TidyWelcomeMsg(char ** pPrompt) +{ + // Make sure Welcome Message doesn't ends with > + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + while (i >= 0 && Prompt[i] == '>') + Prompt[i--] = 0; + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompt(char ** pPrompt) +{ + // Make sure prompt ends > CR LF + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + if (Prompt[i] != '>') + strcat(Prompt, ">"); + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompts() +{ + TidyPrompt(&Prompt); + TidyPrompt(&NewPrompt); + TidyPrompt(&ExpertPrompt); +} + +BOOL SendARQMail(CIRCUIT * conn) +{ + conn->NextMessagetoForward = FirstMessageIndextoForward; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + char MsgHddr[512]; + int HddrLen; + char TimeString[64]; + char * WholeMessage; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + Msg = conn->FwdMsg; + WholeMessage = malloc(Msg->length + 512); + + FormatTime(TimeString, (time_t)Msg->datecreated); + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + Logprintf(LOG_BBS, conn, '>', "ARQ Send Msg %d From %s To %s", Msg->number, Msg->from, Msg->to); + + HddrLen = sprintf(MsgHddr, "Date: %s\nTo: %s\nFrom: %s\nSubject %s\n\n", + TimeString, Msg->to, Msg->from, Msg->title); + + MsgLen = sprintf(WholeMessage, "ARQ:FILE::Msg%s_%d\nARQ:EMAIL::\nARQ:SIZE::%d\nARQ::STX\n%s%s\nARQ::ETX\n", + BBSName, Msg->number, (int)(HddrLen + strlen(MsgBytes)), MsgHddr, MsgBytes); + + WholeMessage[MsgLen] = 0; + QueueMsg(conn,WholeMessage, MsgLen); + + free(WholeMessage); + free(MsgBytes); + + // FLARQ doesn't ACK the message, so set flag to look for all acked + + conn->BBSFlags |= ARQMAILACK; + conn->ARQClearCount = 10; // To make sure clear isn't reported too soon + + return TRUE; + } + + // Nothing to send - close + + Logprintf(LOG_BBS, conn, '>', "ARQ Send - Nothing to Send - Closing"); + + conn->CloseAfterFlush = 20; + return FALSE; +} + +char *stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup (ch1); + chN2 = _strdup (ch2); + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + chNdx = chN2; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + + chNdx = strstr (chN1, chN2); + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + free (chN1); + free (chN2); + return chRet; +} + +#ifdef WIN32 + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\*.*"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + nodeprintf(conn, "No Files\r"); + return; + } + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + nodeprintf(conn, "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); +} + +#else + +#include + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + struct dirent **namelist; + int n, i; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + nodeprintf(conn, "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + return; +} +#endif + +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + char * MsgBytes; + + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Missing Filename\r"); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(MsgBytes); + + nodeprintf(conn, "\r\r[End of File %s]\r", filename); + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); +} + +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + struct TempUserInfo * Temp = user->Temp; + + Buffer[len] = 0; + + // Command entered during listing pause. May be A R or C (or ) + + if (Buffer[0] == 'A' || Buffer[0] == 'a') + { + // Abort + + Temp->ListActive = Temp->ListSuspended = FALSE; + SendPrompt(conn, user); + return; + } + + if (_memicmp(Buffer, "R ", 2) == 0) + { + // Read Message(es) + + int msgno; + char * ptr; + char * Context; + + ptr = strtok_s(&Buffer[2], " ", &Context); + + while (ptr) + { + msgno = atoi(ptr); + ReadMessage(conn, user, msgno); + + ptr = strtok_s(NULL, " ", &Context); + } + + nodeprintf(conn, "bort, , = Continue..>"); + return; + } + + if (Buffer[0] == 'C' || Buffer[0] == 'c' || Buffer[0] == '\r' ) + { + // Resume Listing from where we left off + + DoListCommand(conn, user, Temp->LastListCommand, Temp->LastListParams, TRUE, ""); + SendPrompt(conn, user); + return; + } + + nodeprintf(conn, "bort, , = Continue..>"); + +} +/* +CreateMessageWithAttachments() +{ + int i; + char * ptr, * ptr2, * ptr3, * ptr4; + char Boundary[1000]; + BOOL Multipart = FALSE; + BOOL ALT = FALSE; + int Partlen; + char * Save; + BOOL Base64 = FALSE; + BOOL QuotedP = FALSE; + + char FileName[100][250] = {""}; + int FileLen[100]; + char * FileBody[100]; + char * MallocSave[100]; + UCHAR * NewMsg; + + int Files = 0; + + ptr = Msg; + + if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 2000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sockptr->socket, 0); + return FALSE; + } + } + + + NewMsg = sockptr->MailBuffer + 1000; + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]); + + for (i = 1; i < Files; i++) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + + for (i = 0; i < Files; i++) + { + memcpy(NewMsg, FileBody[i], FileLen[i]); + NewMsg += FileLen[i]; + free(MallocSave[i]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + *MsgLen = NewMsg - (sockptr->MailBuffer + 1000); + *Body = sockptr->MailBuffer + 1000; + + return TRUE; // B2 Message +} + +*/ +VOID CreateUserReport() +{ + struct UserInfo * User; + int i; + char Line[200]; + int len; + char File[MAX_PATH]; + FILE * hFile; + + sprintf(File, "%s/UserList.csv", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + { + Debugprintf("Failed to create UserList.csv"); + return; + } + + for (i=1; i <= NumberofUsers; i++) + { + User = UserRecPtr[i]; + + len = sprintf(Line, "%s,%d,%s,%x,%s,\"%s\",%x,%s,%s,%s\r\n", + User->Call, + User->lastmsg, + FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), + User->flags, + User->Name, + User->Address, + User->RMSSSIDBits, + User->HomeBBS, + User->QRA, + User->ZIP +// struct MsgStats Total; +// struct MsgStats Last; + ); + fwrite(Line, 1, len, hFile); + } + + fclose(hFile); +} + +BOOL ProcessYAPPMessage(CIRCUIT * conn) +{ + int Len = conn->InputLen; + UCHAR * Msg = conn->InputBuffer; + int pktLen = Msg[1]; + char Reply[2] = {ACK}; + int NameLen, SizeLen, OptLen; + char * ptr; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char Mess[255]; + int len; + char * FN = &Msg[2]; + + switch (Msg[0]) + { + case ENQ: // YAPP Send_Init + + // Shouldn't occur in session. Reset state + + Mess[0] = ACK; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + conn->InputLen = 0; + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + return TRUE; + + case SOH: + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + // YAPPC has date/time in dos format + + if (Len < Msg[1] + 1) + return 0; + + NameLen = (int)strlen(FN); + strcpy(conn->ARQFilename, FN); + ptr = &Msg[3 + NameLen]; + SizeLen = (int)strlen(ptr); + FileSize = atoi(ptr); + + // Check file name for unsafe characters (.. / \) + + if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Mess[0] = NAK; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP File Name %s invalid\r", FN); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + OptLen = pktLen - (NameLen + SizeLen + 2); + + conn->YAPPDate = 0; + + if (OptLen >= 8) // We have a Date/Time for YAPPC + { + ptr = ptr + SizeLen + 1; + conn->YAPPDate = strtol(ptr, NULL, 16); + } + + // Check Size + + if (FileSize > MaxRXSize) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + // Make sure file does not exist + + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s already exists\r", conn->ARQFilename);; + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s already exists\r", conn->ARQFilename); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + fclose(hFile); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + + conn->MailBufferSize = FileSize; + conn->MailBuffer=malloc(FileSize); + conn->YAPPLen = 0; + + if (conn->YAPPDate) // If present use YAPPC + Reply[1] = ACK; //Receive_TPK + else + Reply[1] = 2; //Rcv_File + + QueueMsg(conn, Reply, 2); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP upload to %s started", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len, LOG_BBS); + + conn->InputLen = 0; + return FALSE; + + case STX: + + // Data Packet + + // Check we have it all + + if (conn->YAPPDate) // If present use YAPPC so have checksum + { + if (pktLen > (Len - 3)) // -3 for header and checksum + return 0; // Wait for rest + } + else + { + if (pktLen > (Len - 2)) // -2 for header + return 0; // Wait for rest + } + + // Save data and remove from buffer + + // if YAPPC check checksum + + if (conn->YAPPDate) + { + UCHAR Sum = 0; + int i; + UCHAR * uptr = &Msg[2]; + + i = pktLen; + + while(i--) + Sum += *(uptr++); + + if (Sum != *uptr) + { + // Checksum Error + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPPC Checksum Error\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + } + + if ((conn->YAPPLen) + pktLen > conn->MailBufferSize) + { + // Too Big ?? + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP Too much data received\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + + + memcpy(&conn->MailBuffer[conn->YAPPLen], &Msg[2], pktLen); + conn->YAPPLen += pktLen; + + if (conn->YAPPDate) + ++pktLen; // Add Checksum + + conn->InputLen -= (pktLen + 2); + memmove(conn->InputBuffer, &conn->InputBuffer[pktLen + 2], conn->InputLen); + + return TRUE; + + case ETX: + + // End Data + + + + if (conn->YAPPLen == conn->MailBufferSize) + { + // All received + + int ret; + DWORD Written = 0; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + +#ifdef WIN32 + hFile = CreateFile(MsgFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + ret = WriteFile(hFile, conn->MailBuffer, conn->YAPPLen, &Written, NULL); + + if (conn->YAPPDate) + { + FILETIME FileTime; + struct tm TM; + struct timeval times[2]; + time_t TT; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + TT = mktime(&TM); + times[0].tv_sec = times[1].tv_sec = + times[0].tv_usec = times[1].tv_usec = 0; + + DosDateTimeToFileTime((WORD)(conn->YAPPDate >> 16), (WORD)conn->YAPPDate & 0xFFFF, &FileTime); + ret = SetFileTime(hFile, &FileTime, &FileTime, &FileTime); + ret = GetLastError(); + + } + CloseHandle(hFile); + } +#else + + hFile = fopen(MsgFile, "wb"); + if (hFile) + { + Written = fwrite(conn->MailBuffer, 1, conn->YAPPLen, hFile); + fclose(hFile); + + if (conn->YAPPDate) + { + struct tm TM; + struct timeval times[2]; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + times[0].tv_sec = times[1].tv_sec = mktime(&TM); + times[0].tv_usec = times[1].tv_usec = 0; + } + } +#endif + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (Written != conn->YAPPLen) + { + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "Failed to save YAPP File\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + } + } + + Reply[1] = 3; //Ack_EOF + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + + return TRUE; + + case EOT: + + // End Session + + Reply[1] = 4; // Ack_EOT + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + conn->InputMode = 0; + + len = sprintf_s(Mess, sizeof(Mess), "YAPP file %s received\r", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + + return TRUE; + + case CAN: + + // Abort + + Mess[0] = ACK; + Mess[1] = 5; // CAN Ack + QueueMsg(conn, Mess, 2); + Flush(conn); + + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + + // There may be a reason after the CAN + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + + len = sprintf_s(Mess, sizeof(Mess), "YAPP Transfer cancelled by Terminal\r"); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + return FALSE; + + case ACK: + + switch (Msg[1]) + { + case 1: // Rcv_Rdy + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + len = (int)strlen(conn->ARQFilename) + 3; + + strcpy(&Mess[2], conn->ARQFilename); + len += sprintf(&Mess[len], "%d", conn->MailBufferSize); + len++; // include null + Mess[0] = SOH; + Mess[1] = len - 2; + + QueueMsg(conn, Mess, len); + Flush(conn); + conn->InputLen = 0; + + return FALSE; + + case 2: + + // Start sending message + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + case 3: + + // ACK EOF - Send EOT + + + Mess[0] = EOT; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputLen = 0; + return FALSE; + + case 4: + + // ACK EOT + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + conn->InputLen = 0; + return FALSE; + + default: + conn->InputLen = 0; + return FALSE; + + + + } + + case NAK: + + // Either Reject or Restart + + // RE Resume NAK len R NULL (File size in ASCII) NULL + + if (conn->InputLen > 2 && Msg[2] == 'R' && Msg[3] == 0) + { + int posn = atoi(&Msg[4]); + + conn->YAPPLen += posn; + conn->MailBufferSize -= posn; + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + } + + // There may be a reason after the ack + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + return FALSE; + } + + nodeprintf(conn, "Unexpected message during YAPP Transfer. Transfer canncelled\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + + return FALSE; + +} + +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Filename missing\r"); + SendPrompt(conn, user); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + SendPrompt(conn, user); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + char Mess[255]; + strcpy(conn->ARQFilename, filename); + conn->MailBuffer = malloc(FileSize); + conn->MailBufferSize = FileSize; + conn->YAPPLen = 0; + fread(conn->MailBuffer, 1, FileSize, hFile); + fclose(hFile); + + Mess[0] = ENQ; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputMode = 'Y'; + + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); + SendPrompt(conn, user); +} + +void YAPPSendData(ConnectionInfo * conn) +{ + char Mess[258]; + + conn->BBSFlags |= YAPPTX; + + while (TXCount(conn->BPQStream) < 15) + { + int Left = conn->MailBufferSize; + + if (Left == 0) + { + // Finished - send End Data + + Mess[0] = ETX; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->BBSFlags &= ~YAPPTX; + break; + } + + if (Left > conn->paclen - 2) // 2 byte header + Left = conn->paclen -2; + + memcpy(&Mess[2], &conn->MailBuffer[conn->YAPPLen], Left); + Mess[0] = STX; + Mess[1] = Left; + + QueueMsg(conn, Mess, Left + 2); + Flush(conn); + + conn->YAPPLen += Left; + conn->MailBufferSize -= Left; + } +} + +char * AddUser(char * Call, char * password, BOOL BBSFlag) +{ + struct UserInfo * USER; + + strlop(Call, '-'); + + if (strlen(Call) > 6) + Call[6] = 0; + + _strupr(Call); + + if (Call[0] == 0 || LookupCall(Call)) + { + return("User already exists\r\n"); + } + + USER = AllocateUserRecord(Call); + USER->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (strlen(password) > 12) + password[12] = 0; + + strcpy(USER->pass, password); + + if (BBSFlag) + { + if(SetupNewBBS(USER)) + USER->flags |= F_BBS; + else + printf("Cannot set user to be a BBS - you already have 160 BBS's defined\r\n"); + } + + SaveUserDatabase(); + UpdateWPWithUserInfo(USER); + + return("User added\r\n"); +} + +// Server Support Code + +// For the moment only internal REQDIR and REQFIL. + +// May add WPSERV and user implemented servers +/* +F6FBB BBS > + SP REQDIR @ F6ABJ.FRA.EU + Title of message : + YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + F6FBB BBS > + SP REQFIL @ F6ABJ.FRA.EU + Title of message : + DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + Note Text not used. + +*/ + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To); + +BOOL ProcessReqDir(struct MsgInfo * Msg) +{ + char * Buffer; + int Len = 0; + char * ptr; + + // Parse title - gives directory and return address + + // YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char Pattern[64]; + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + +#ifdef WIN32 + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + +#else + + #include + + struct dirent **namelist; + int n, i; + struct stat STAT; + int res; + char FN[256]; + +#endif + + strcpy(Pattern, Msg->title); + + ptr = strchr(Pattern, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(Pattern, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize); + +#ifdef WIN32 + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\"); + strcat(szDir, Pattern); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + Len = sprintf(Buffer, "No Files\r"); + } + else + { + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + Len += sprintf(&Buffer[Len], "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + } + +#else + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + Len += sprintf(&Buffer[Len], "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + +#endif + + // Build Message + + SendServerReply("REQDIR Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +/* + ' Augment Message ID with the Message Pickup Station we're directing this message to. + ' + Dim strAugmentedMessageID As String + If GetMidRMS(MessageId) <> "" Then + ' The MPS RMS is already set on the message ID + strAugmentedMessageID = MessageId + strMPS = GetMidRMS(MessageId) + ' "@R" at the end of the MID means route message only via radio + If GetMidForwarding(MessageId) = "" And (blnRadioOnly Or UploadThroughInternet()) Then + strAugmentedMessageID &= "@" & strHFOnlyFlag + End If + ElseIf strMPS <> "" Then + ' Add MPS to the message ID + strAugmentedMessageID = MessageId & "@" & strMPS + ' "@R" at the end of the MID means route message only via radio + If blnRadioOnly Or UploadThroughInternet() Then + strAugmentedMessageID &= "@" & strHFOnlyFlag + End If + Else + strAugmentedMessageID = MessageId + End If + +*/ + +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + Buffer[len] = 0; + + if (conn->Flags & GETTINGSYNCMESSAGE) + { + // Data + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + if (conn->TempMsg->length >= conn->SyncCompressedLen) + { + // Complete - decompress it + + conn->BBSFlags |= FBBCompressed; + Decode(conn, 1); + + conn->Flags &= !GETTINGSYNCMESSAGE; + + BBSputs(conn, "OK\r"); + return; + } + return; + } + + if (conn->Flags & PROPOSINGSYNCMSG) + { + // Waiting for response to TR AddMessage + + if (strcmp(Buffer, "OK\r") == 0) + { + char Msg[256]; + int n; + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Send the message, it has already been built + + conn->Flags &= !PROPOSINGSYNCMSG; + conn->Flags |= SENDINGSYNCMSG; + + n = sprintf_s(Msg, sizeof(Msg), "Sending SYNC message %s", conn->FwdMsg->bid); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + QueueMsg(conn, conn->SyncMessage, conn->SyncCompressedLen); + return; + } + + if (strcmp(Buffer, "NO\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Message Rejected - ? duplicate + + if (conn->FwdMsg) + { + // Zap the entry + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + } + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->Flags &= !PROPOSINGSYNCMSG; + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (conn->Flags & SENDINGSYNCMSG) + { + if (strcmp(Buffer, "OK\r") == 0) + { + // Message Sent + + conn->Flags &= !SENDINGSYNCMSG; + free(conn->SyncMessage); + + if (conn->FwdMsg) + { + char Msg[256]; + int n; + + n = sprintf_s(Msg, sizeof(Msg), "SYNC message %s Sent", conn->FwdMsg->bid); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + + // drop through to send any more + } + else + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + conn->Flags &= !SENDINGSYNCMSG; + free(conn->SyncMessage); + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + + return; + } + } + + if (strcmp(Buffer, "OK\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Send Message(?s) to RMS Relay SYNC + +/* +OK +>TR AddMessage_V5JLSGH591JR 786 1219 522 True +BYE*/ + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg = conn->FwdMsg; + char Buffer[128]; + char * Message; + + Message = FormatSYNCMessage(conn, Msg); + + // Need to compress it + + conn->SyncMessage = malloc(conn->SyncXMLLen + conn->SyncMsgLen + 4096); + + conn->SyncCompressedLen = Encode(Message, conn->SyncMessage, conn->SyncXMLLen + conn->SyncMsgLen, 0, 1); + + sprintf(Buffer, "TR AddMessage_%s %d %d %d True\r", // The True on end indicates compressed + Msg->bid, conn->SyncCompressedLen, conn->SyncXMLLen, conn->SyncMsgLen); + + free(Message); + + conn->Flags |= PROPOSINGSYNCMSG; + + BBSputs(conn, Buffer); + return; + } + + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "TR ", 2) == 0) + { + // Messages have TR_COMMAND_BID Compressed Len XML Len Bosy Len + + char * Command; + char * BIDptr; + + BIDRec * BID; + char *ptr1, *ptr2, *context; + + // TR AddMessage_1145_G8BPQ 727 1202 440 True + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + Command = strtok_s(&Buffer[3], "_", &context); + BIDptr = strtok_s(NULL, " ", &context); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncCompressedLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncXMLLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncMsgLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + + // If addmessage need to check bid doesn't exist + + if (strcmp(Command, "AddMessage") == 0) + { + strlop(BIDptr, '@'); // sometimes has @CALL@R + if (strlen(BIDptr) > 12) + BIDptr[12] = 0; + + BID = LookupBID(BIDptr); + + if (BID) + { + BBSputs(conn, "Rejected - Duplicate BID\r"); + return; + } + } + + conn->TempMsg = zalloc(sizeof(struct MsgInfo)); + + conn->Flags |= GETTINGSYNCMESSAGE; + + BBSputs(conn, "OK\r"); + return; + } + + if (memcmp(Buffer, "BYE\r", 4) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "BBS\r", 4) == 0) + { + // Out of Sync + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags &= ~SYNCMODE; + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + WriteLogLine(conn, '<', "Unexpected SYNC Message", 23, LOG_BBS); + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; +} +BOOL ProcessReqFile(struct MsgInfo * Msg) +{ + char FN[128]; + char * Buffer; + int Len = 0; + char * ptr; + struct stat STAT; + char MsgFile[MAX_PATH]; + FILE * hFile; + int FileSize; + char * MsgBytes; + + // Parse title - gives file and return address + + // DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + + strcpy(FN, Msg->title); + + ptr = strchr(FN, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(FN, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize + 1); // Allow terminating Null + + // Build Message + + if (FN == NULL) + { + Len = sprintf(Buffer, "Missing Filename\r"); + } + else if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Len = sprintf(Buffer,"Invalid filename %s\r", FN); + } + else + { + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, FN); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", FN); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + if (FileSize > MaxTXSize) + FileSize = MaxTXSize; // Truncate to max size + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + Len = sprintf(Buffer, "%s", MsgBytes); + free(MsgBytes); + } + } + else + Len = sprintf(Buffer, "File %s not found\r", FN); + } + + SendServerReply("REQFIL Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +BOOL CheckforMessagetoServer(struct MsgInfo * Msg) +{ + if (_stricmp(Msg->to, "REQDIR") == 0) + return ProcessReqDir(Msg); + + if (_stricmp(Msg->to, "REQFIL") == 0) + return ProcessReqFile(Msg); + + return FALSE; +} + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + char * Via; + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, BBSName); + Via = strlop(To, '@'); + + if (Via) + strcpy(Msg->via, Via); + + strcpy(Msg->to, To); + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +void SendRequestSync(CIRCUIT * conn) +{ + // Only need XML Header + + char * Buffer = malloc(4096); + int Len = 0; + + struct tm *tm; + char Date[32]; + char MsgTime[32]; + time_t Time = time(NULL); + + char * Encoded; + + tm = gmtime(&Time); + + sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + Len += sprintf(&Buffer[Len], "\r\n"); + + Len += sprintf(&Buffer[Len], "\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " request_sync\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Date); + Len += sprintf(&Buffer[Len], " %s\r\n", BBSName); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " BBSName\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", conn->SyncHost); + Len += sprintf(&Buffer[Len], " %d\r\n", conn->SyncPort); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); + +/* + + + + request_sync + 20230205100652 + GI8BPQ + + + GI8BPQ + + 127.0.0.1 + 8780 + + + +*/ + + // Need to compress it + + conn->SyncXMLLen = Len; + conn->SyncMsgLen = 0; + + conn->SyncMessage = malloc(conn->SyncXMLLen + 4096); + + conn->SyncCompressedLen = Encode(Buffer, conn->SyncMessage, conn->SyncXMLLen, 0, 1); + + sprintf(Buffer, "TR RequestSync_%s_%d %d %d 0 True\r", // The True on end indicates compressed + 50, conn->SyncCompressedLen, conn->SyncXMLLen); + + free(Buffer); + + conn->Flags |= REQUESTINGSYNC; + + BBSputs(conn, Buffer); + return; +} + + +void ProcessSyncXML(CIRCUIT * conn, char * XML) +{ + // Process XML from RMS Relay Sync + + // All seem to start + + // + // + // + // + + char * Type = strstr(XML, ""); + + if (Type == NULL) + return; + + Type += strlen(""); + + if (memcmp(Type, "rms_location", 12) == 0) + { + return; + } + + + if (memcmp(Type, "request_sync", 12) == 0) + { + char * Call; + struct UserInfo * BBSREC; + + // This isn't requesting a poll, it is asking to be added as a sync partner + + Call = strstr(Type, ""); + + if (Call == NULL) + return; + + Call += 10; + strlop(Call, '<'); + BBSREC = FindBBS(Call); + + if (BBSREC == NULL) + return; + + if (BBSREC->ForwardingInfo->Forwarding == 0) + StartForwarding(BBSREC->BBSNumber, NULL); + + return; + } + + if (memcmp(Type, "remove_message", 14) == 0) + { + char * MID = strstr(Type, ""); + struct MsgInfo * Msg; + + if (MID == NULL) + return; + + MID += 11; + strlop(MID, '<'); + + strlop(MID, '@'); // sometimes has @CALL@R + if (strlen(MID) > 12) + MID[12] = 0; + + Msg = FindMessageByBID(MID); + + if (Msg == NULL) + return; + + Logprintf(LOG_BBS, conn, '|', "Killing Msg %d %s", Msg->number, Msg->bid); + + FlagAsKilled(Msg, TRUE); + return; + } + + if (memcmp(Type, "delivered", 9) == 0) + { + char * MID = strstr(Type, ""); + struct MsgInfo * Msg; + + if (MID == NULL) + return; + + MID += 11; + strlop(MID, '<'); + + strlop(MID, '@'); // sometimes has @CALL@R + if (strlen(MID) > 12) + MID[12] = 0; + + Msg = FindMessageByBID(MID); + + if (Msg == NULL) + return; + + Logprintf(LOG_BBS, conn, '|', "Message Msg %d %s Delivered", Msg->number, Msg->bid); + return; + } + + Debugprintf(Type); + return; + +/* + + + + request_sync + 20230205100652 + GI8BPQ + + + GI8BPQ + + 127.0.0.1 + 8780 + + + +} + + + + delivered + 20230205093113 + G8BPQ + + + 10845_GM8BPB + G8BPQ + G8BPQ + 3 + + + + Public Enum MessageDeliveryMethod + ' + ' Method used to deliver a message. None if the message hasn't been delivered. + ' + Unspecified = -1 + None = 0 + Telnet = 1 + CMS = 2 + Radio = 3 + Email = 4 +End Enum +*/ +} + +int ReformatSyncMessage(CIRCUIT * conn) +{ + // Message has been decompressed - reformat to look like a WLE message + + char * MsgBit; + char *ptr1, *ptr2; + int linelen; + char FullFrom[80]; + char FullTo[80]; + char BID[80]; + time_t Date; + char Mon[80]; + char Subject[80]; + int i = 0; + char * Boundary; + char * Input; + char * via = NULL; + char * NewMsg = conn->MailBuffer; + char * SaveMsg = NewMsg; + char DateString[80]; + struct tm * tm; + char Type[16] = "Private"; + char * part[100] = {""}; + char * partname[100]; + int partLen[100]; + char xml[4096]; + + // Message has an XML header then the message + + // The XML may have control info, so examine it. + + /* + Date: Mon, 25 Oct 2021 10:22:00 -0000 + From: GM8BPQ + Subject: Test + To: 2E1BGT + Message-ID: ALYJQJRXVQAO + X-Source: GM8BPQ + X-Relay: G8BPQ + MIME-Version: 1.0 + MIME-Version: 1.0 + Content-Type: multipart/mixed; boundary="boundaryBSoxlw==" + + --boundaryBSoxlw== + Content-Type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Hello Hello + + --boundaryBSoxlw==-- + */ + + // I think the best way is to reformat as if from Winlink Express, then pass + //through the normal B2 code. + +// WriteLogLine(conn, '<', conn->MailBuffer, conn->TempMsg->length, LOG_BBS); + + // display the message for testing + + conn->MailBuffer[conn->TempMsg->length] = 0; + +// OutputDebugString(conn->MailBuffer); + memcpy(xml, conn->MailBuffer, conn->SyncXMLLen); + xml[conn->SyncXMLLen] = 0; + + if (conn->SyncMsgLen == 0) + { + // No message, Just xml. Looks like a status report + + ProcessSyncXML(conn, xml); + return 0; + } + + MsgBit = &conn->MailBuffer[conn->SyncXMLLen]; + conn->TempMsg->length -= conn->SyncXMLLen; + + ptr1 = MsgBit; + +Loop: + + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0) + { + memcpy(FullFrom, &ptr1[6], linelen - 6); + FullFrom[linelen - 6] = 0; + } + + if (_memicmp(ptr1, "To:", 3) == 0) + { + memcpy(FullTo, &ptr1[4], linelen - 4); + FullTo[linelen - 4] = 0; + } + + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + memcpy(Subject, &ptr1[9], linelen - 9); + Subject[linelen - 9] = 0; + } + + else if (_memicmp(ptr1, "Message-ID", 10) == 0) + { + memcpy(BID, &ptr1[12], linelen - 12); + BID[linelen - 12] = 0; + } + + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Mon, 25 Oct 2021 10:22:00 -0000 + + sscanf(&ptr1[11], "%02d %s %04d %02d:%02d:%02d", + &rtime.tm_mday, &Mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + for (i = 0; i < 12; i++) + { + if (strcmp(Mon, month[i]) == 0) + break; + } + + rtime.tm_mon = i; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + // Unpack Body - seems to be multipart even if only one + + // Can't we just send the whole body through ?? + // No, Attachment format is different + + // Mbo: GM8BPQ + // Body: 17 + // File: 1471 leadercoeffs.txt + + Input = MsgBit; + Boundary = initMultipartUnpack(&Input); + + i = 0; + + if (Boundary) + { + // input should be start of part + + // Find End of part - ie -- Boundary + CRLF or -- + + char * ptr, * saveptr; + char * Msgptr; + size_t BLen = strlen(Boundary); + size_t Partlen; + + saveptr = Msgptr = ptr = Input; + + while(ptr) // Just in case we run off end + { + if (*ptr == '-' && *(ptr+1) == '-') + { + if (memcmp(&ptr[2], Boundary, BLen) == 0) + { + // Found Boundary + + char * p1, *p2, *ptr3, *ptr4; + int llen; + int Base64 = 0; + int QuotedP = 0; + char * BoundaryStart = ptr; + + Partlen = ptr - Msgptr; + + ptr += (BLen + 2); // End of Boundary + + if (*ptr == '-') // Terminating Boundary + Input = NULL; + else + Input = ptr + 2; + + // Will check for quoted printable + + p1 = Msgptr; +Loop2: + p2 = strchr(p1, '\r'); + llen = (int)(p2 - p1); + + if (llen) + { + + if (_memicmp(p1, "Content-Transfer-Encoding:", 26) == 0) + { + if (_memicmp(&p1[27], "base64", 6) == 0) + Base64 = TRUE; + else if (_memicmp(&p1[27], "quoted", 6) == 0) + QuotedP = TRUE; + } + else if (_memicmp(p1, "Content-Disposition: ", 21) == 0) + { + ptr3 = strstr(&p1[21], "name"); + + if (ptr3) + { + ptr3 += 5; + if (*ptr3 == '"') ptr3++; + ptr4 = strchr(ptr3, '"'); + if (ptr4) *ptr4 = 0; + + partname[i] = ptr3; + } + } + + if (llen) // Not Null line + { + p1 = p2 + 2; // Skip crlf + goto Loop2; + } + } + + part[i] = strstr(p2, "\r\n"); // Over separator + + if (part[i]) + { + part[i] += 2; + partLen[i] = BoundaryStart - part[i] - 2; + if (QuotedP) + partLen[i] = decode_quoted_printable(part[i], partLen[i]); + else if (Base64) + { + int Len = partLen[i], NewLen; + char * ptr = part[i]; + char * ptr2 = part[i]; + + // WLE sends base64 with embedded crlf, so remove them + + while (Len-- > 0) + { + if ((*ptr) != 10 && (*ptr) != 13) + *(ptr2++) = *(ptr++); + else + ptr ++; + } + + Len = ptr2 - part[i]; + ptr = part[i]; + ptr2 = part[i]; + + while (Len > 0) + { + decodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - part[i]); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + partLen[i] = NewLen; + } + } + Msgptr = ptr = Input; + i++; + continue; } + + // See if more parts + } + ptr++; + } + ptr++; + } + + + // Build the message + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + NewMsg += sprintf(NewMsg, + "MID: %s\r\n" + "Date: %s\r\n" + "Type: %s\r\n" + "From: %s\r\n", + BID, DateString, Type, FullFrom); + +// if (ToCalls) +// { +// int i; + +// for (i = 0; i < Calls; i++) +// NewMsg += sprintf(NewMsg, "To: %s\r\n", ToCalls[i]); + +// } +// else + { + NewMsg += sprintf(NewMsg, "To: %s\r\n", + FullTo); + } +// if (WebMail->CC && WebMail->CC[0]) +// NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC); + + NewMsg += sprintf(NewMsg, + "Subject: %s\r\n" + "Mbo: %s\r\n", + Subject, BBSName); + + // Write the Body: line and any File Lines + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", partLen[0]); + + i = 1; + + while (part[i]) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", + partLen[i], partname[i]); + + i++; + } + + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header + + // Now add parts + + i = 0; + + while (part[i]) + { + memmove(NewMsg, part[i], partLen[i]); + NewMsg += partLen[i]; + i++; + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line between attachments + } + + conn->TempMsg->length = NewMsg - SaveMsg; + conn->TempMsg->datereceived = conn->TempMsg->datechanged = time(NULL); + conn->TempMsg->datecreated = Date; + strcpy(conn->TempMsg->bid, BID); + + if (strlen(Subject) > 60) + Subject[60] = 0; + + strcpy(conn->TempMsg->title, Subject); + + return TRUE; +} + +char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg) +{ + // First an XML Header + + char * Buffer = malloc(4096 + Msg->length); + int Len = 0; + + struct tm *tm; + char Date[32]; + char MsgTime[32]; + char Separator[33]=""; + time_t Time = time(NULL); + char * MailBuffer; + int BodyLen; + char * Encoded; + + // Get the message - may need length in header + + MailBuffer = ReadMessageFile(Msg->number); + + BodyLen = Msg->length; + + // Remove any B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MailBuffer, "Body:"); + if (ptr) + { + BodyLen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + { + memcpy(MailBuffer, ptr + 4, BodyLen); + MailBuffer[BodyLen] = 0; + } + } + + // encode body as quoted printable; + + Encoded = malloc(Msg->length * 3); + + BodyLen = encode_quoted_printable(MailBuffer, Encoded, BodyLen); + + // Create multipart Boundary + + CreateOneTimePassword(&Separator[0], "Key", 0); + CreateOneTimePassword(&Separator[16], "Key", 1); + + + tm = gmtime(&Time); + + sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + Len += sprintf(&Buffer[Len], "\r\n"); + + Len += sprintf(&Buffer[Len], "\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " add_message\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Date); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); + Len += sprintf(&Buffer[Len], " \r\n", MsgTime); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " 2\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", (Msg->B2Flags & Attachments) ? "true" : "false"); + Len += sprintf(&Buffer[Len], " %d\r\n", BodyLen); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->title); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); + Len += sprintf(&Buffer[Len], " 450443\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->to); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " 0\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " True\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); + +// Debugprintf(Buffer); + + conn->SyncXMLLen = Len; + + Len += sprintf(&Buffer[Len], "Date: Sat, 04 Feb 2023 11:19:00 +0000\r\n"); + Len += sprintf(&Buffer[Len], "From: %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], "Subject: %s\r\n", Msg->title); + Len += sprintf(&Buffer[Len], "To: %s\r\n", Msg->to); + Len += sprintf(&Buffer[Len], "Message-ID: %s\r\n", Msg->bid); +// Len += sprintf(&Buffer[Len], "X-Source: G8BPQ\r\n"); +// Len += sprintf(&Buffer[Len], "X-Location: 52.979167N, 1.125000W (GRID SQUARE)\r\n"); +// Len += sprintf(&Buffer[Len], "X-RMS-Originator: G8BPQ\r\n"); +// Len += sprintf(&Buffer[Len], "X-RMS-Path: G8BPQ@2023-02-04-11:19:29\r\n"); + Len += sprintf(&Buffer[Len], "X-Relay: %s\r\n", BBSName); + + Len += sprintf(&Buffer[Len], "MIME-Version: 1.0\r\n"); + Len += sprintf(&Buffer[Len], "Content-Type: multipart/mixed; boundary=\"%s\"\r\n", Separator); + + Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before separator + Len += sprintf(&Buffer[Len], "--%s\r\n", Separator); + Len += sprintf(&Buffer[Len], "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n"); + Len += sprintf(&Buffer[Len], "Content-Transfer-Encoding: quoted-printable\r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before body + + Len += sprintf(&Buffer[Len], "%s\r\n", Encoded); + Len += sprintf(&Buffer[Len], "--%s--\r\n", Separator); + + conn->SyncMsgLen = Len - conn->SyncXMLLen; + + free(Encoded); + free(MailBuffer); + + return Buffer; +} + +int encode_quoted_printable(char *s, char * out, int Len) +{ + int n = 0; + char * start = out; + + while(Len--) + { + if (n >= 73 && *s != 10 && *s != 13) + {strcpy(out, "=\r\n"); n = 0; out +=3;} + if (*s == 10 || *s == 13) {putchar(*s); n = 0;} + else if (*s<32 || *s==61 || *s>126) + out += sprintf(out, "=%02x", (unsigned char)*s); + else if (*s != 32 || (*(s+1) != 10 && *(s+1) != 13)) + {*(out++) = *s; n++;} + else n += printf("=20"); + + s++; + } + *out = 0; + + return out - start; +} + +int decode_quoted_printable(char *ptr, int len) +{ + // overwrite input with decoded version + + char * ptr2 = ptr; + char * End = ptr + len; + char * Start = ptr; + + while (ptr < End) + { + if ((*ptr) == '=') + { + char c = *(++ptr); + char d; + + c = c - 48; + if (c < 0) + { + // = CRLF as a soft break + + ptr += 2; + continue; + } + if (c > 9) c -= 7; + d = *(++ptr); + d = d - 48; + if (d > 9) d -= 7; + + *(ptr2) = c << 4 | d; + ptr2++; + ptr++; + } + else + *ptr2++ = *ptr++; + } + return ptr2 - Start; +} diff --git a/BPQMail.c b/BPQMail.c index a4eecaf..82e58bc 100644 --- a/BPQMail.c +++ b/BPQMail.c @@ -1355,6 +1355,7 @@ BOOL CreateMulticastConsole(); char * CheckToAddress(CIRCUIT * conn, char * Addr); BOOL CheckifPacket(char * Via); int GetHTMLForms(); +VOID GetPGConfig(); struct _EXCEPTION_POINTERS exinfox; @@ -1708,6 +1709,14 @@ int APIENTRY WinMain(HINSTANCE hInstance, FreeForwardingStruct(user); free(user->ForwardingInfo); } + /* ---------- TAJ --PG Server------*/ + + if ( user->Temp->RUNPGPARAMS ) { + + printf("Also freeing RUNPGARGS\n"); + free(user->Temp->RUNPGPARAMS); + } + /* --------------------------------*/ free(user->Temp); @@ -3003,9 +3012,9 @@ int RefreshMainWindow() #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -SOCKADDR_IN local_sin; /* Local socket - internet style */ +static SOCKADDR_IN local_sin; /* Local socket - internet style */ -PSOCKADDR_IN psin; +static PSOCKADDR_IN psin; SOCKET sock; @@ -3333,6 +3342,7 @@ BOOL Initialise() // CreateWPReport(); CreatePipeThread(); + GetPGConfig(); return TRUE; } diff --git a/BPQMail.c.bak b/BPQMail.c.bak new file mode 100644 index 0000000..ff79254 --- /dev/null +++ b/BPQMail.c.bak @@ -0,0 +1,3651 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// + +// Version 1.0.0.17re + +// Split Messasge, User and BBS Editing from Main Config. +// Add word wrap to Console input and output +// Flash Console on chat user connect +// Fix processing Name response in chat mode +// Fix processing of *RTL from station not defined as a Chat Node +// Fix overlength lines ln List responses +// Housekeeping expires BIDs +// Killing a message removes it from the forwarding counts + +// Version 1.0.0.18 + +// Save User Database when name is entered or updated so it is not lost on a crash +// Fix Protocol Error in Compressed Forwarding when switching direction +// Add Housekeeping results dialog. + +// Version 1.0.0.19 + +// Allow PACLEN in forward scripts. +// Store and forward messages with CRLF as line ends +// Send Disconnect after FQ ( for LinFBB) +// "Last Listed" is saved if MailChat is closed without closing Console +// Maximum acceptable message length can be specified (in Forwarding Config) + +// Version 1.0.0.20 + +// Fix error in saving forwarding config (introduced in .19) +// Limit size of FBB forwarding block. +// Clear old connection (instead of new) if duplicate connect on Chat Node-Node link +// Send FA for Compressed Mail (was sending FB for both Compressed and Uncompressed) + +// Version 1.0.0.21 + +// Fix Connect Script Processing (wasn't waiting for CONNECTED from last step) +// Implement Defer +// Fix MBL-style forwarding +// Fix Add User (Params were not saved) +// Add SC (Send Copy) Command +// Accept call@bbs as well as call @ bbs + +// Version 1.0.0.22 + +// Implement RB RP LN LR LF LN L$ Commands. +// Implement QTH and ZIP Commands. +// Entering an empty Title cancels the message. +// Uses HomeBBS field to set @ field for local users. +// Creates basic WP Database. +// Uses WP to lookup @ field for non-local calls. +// Console "Actions" Menu renamed "Options". +// Excluded flag is actioned. +// Asks user to set HomeBBS if not already set. +// Fix "Shrinking Message" problem, where message got shorter each time it was read Initroduced in .19). +// Flash Server window when anyone connects to chat (If Console Option "Flash on Chat User Connect" set). + +// Version 1.0.0.23 + +// Fix R: line scan bug + +// Version 1.0.0.24 + +// Fix closing console window on 'B'. +// Fix Message Creation time. +// Enable Delete function in WP edit dialog + +// Version 1.0.0.25 + +// Implement K< and K> commands +// Experimental support for B1 and B2 forwarding +// Experimental UI System +// Fix extracting QTH from WP updates + +// Version 1.0.0.26 + +// Add YN etc responses for FBB B1/B2 + +// Version 1.0.0.27 + +// Fix crash if NULL received as start of a packet. +// Add Save WP command +// Make B2 flag BBS-specific. +// Implement B2 Send + +// Version 1.0.0.28 + +// Fix parsing of smtp to addresses - eg smtp:john.wiseman@cantab.net +// Flag messages as Held if smtp server rejects from or to addresses +// Fix Kill to (K> Call) +// Edit Message dialog shows latest first +// Add chat debug window to try to track down occasional chat connection problems + +// Version 1.0.0.29 + +// Add loads of try/excspt + +// Version 1.0.0.30 + +// Writes Debug output to LOG_DEBUG_X and Monitor Window + +// Version 1.0.0.32 + +// Allow use of GoogleMail for ISP functions +// Accept SYSOP as alias for SYSOPCall - ie user can do SP SYSOP, and it will appear in sysop's LM, RM, etc +// Email Housekeeping Results to SYSOP + +// Version 1.0.0.33 + +// Housekeeping now runs at Maintenance Time. Maintenance Interval removed. +// Allow multiple numbers on R and K commands +// Fix L command with single number +// Log if Forward count is out of step with messages to forward. +// UI Processing improved and F< command implemented + +// Version 1.0.0.34 + +// Semaphore Chat Messages +// Display Semaphore Clashes +// More Program Error Traps +// Kill Messages more than BIDLifetime old + +// Version 1.0.0.35 + +// Test for Mike - Remove B1 check from Parse_SID + +// Version 1.0.0.36 + +// Fix calculation of Housekeeping Time. +// Set dialog box background explicitly. +// Remove tray entry for chat debug window. +// Add date to log file name. +// Add Actions Menu option to disable logging. +// Fix size of main window when it changes between versions. + +// Version 1.0.0.37 + +// Implement Paging. +// Fix L< command (was giving no messages). +// Implement LR LR mmm-nnn LR nnn- (and L nnn-) +// KM should no longer kill SYSOP bulls. +// ISP interfaces allows SMTP Auth to be configured +// SMTP Client would fail to send any more messages if a connection failed + +// Version 1.0.0.38 + +// Don't include killed messages in L commands (except LK!) +// Implement l@ +// Add forwarding timebands +// Allow resizing of main window. +// Add Ver command. + +// Version 1.0.1.1 + +// First Public Beta + +// Fix part line handling in Console +// Maintenance deletes old log files. +// Add option to delete files to the recycle bin. + +// Version 1.0.2.1 + +// Allow all Node SYSOP commands in connect scripts. +// Implement FBB B1 Protocol with Resume +// Make FBB Max Block size settable for each BBS. +// Add extra logging when Chat Sessions refused. +// Fix Crash on invalid housekeeping override. +// Add Hold Messages option. +// Trap CRT Errors +// Sort Actions/Start Forwarding List + +// Version 1.0.2.2 + +// Fill in gaps in BBS Number sequence +// Fix PE if ctext contains } +// Run Houskeeping at startup if previous Housekeeping was missed + +// Version 1.0.2.3 + +// Add configured nodes to /p listing + +// Version 1.0.2.4 + +// Fix RMS (it wanted B2 not B12) +// Send messages if available after rejecting all proposals +// Dont try to send msg back to originator. + +// Version 1.0.2.5 + +// Fix timeband processing when none specified. +// Improved Chat Help display. +// Add helpful responses to /n /q and /t + +// Version 1.0.2.6 + +// Kill Personal WP messages after processing +// Make sure a node doesnt try to "join" or "leave" a node as a user. +// More tracing to try to track down lost topic links. +// Add command recall to Console +// Show users in new topic when changing topic +// Add Send From Clipboard" Action + +// Version 1.0.2.7 + +// Hold messages from the future, or with invalid dates. +// Add KH (kill held) command. +// Send Message to SYSOP when a new user connects. + +// Version 1.0.2.8 + +// Don't reject personal message on Dup BID unless we already have an unforwarded copy. +// Hold Looping messages. +// Warn SYSOP of held messages. + +// Version 1.0.2.9 + +// Close connecton on receipt of *** DONE (MBL style forwarding). +// Improved validation in link_drop (Chat Node) +// Change to welcome prompt and Msg Header for Outpost. +// Fix Connect Script processing for KA Nodes + +// Version 1.0.3.1 + +// Fix incorrect sending of NO - BID. +// Fix problems caused by a user being connected to more than one chat node. +// Show idle time on Chat /u display. +// Rewrite forwarding by HA. +// Add "Bad Words" Test. +// Add reason for holding to SYSOP "Message Held" Message. +// Make topics case-insensitive. +// Allow SR for smtp mail. +// Try to fix some user's "Add User" problem. + + +// Version 1.0.3.2 + +// Fix program error when prcessing - response in FBB forwarding. +// Fix code to flag messages as sent. + + +// Version 1.0.3.3 + +// Attempt to fix message loop on topic_change +// Fix loop if compressed size is greater than 32K when receiving with B1 protocol. +// Fix selection of B1 + +// Version 1.0.3.4 + +// Add "KISS ONLY" Flag to R: Lines (Needs Node Version 4.10.12 (4.10l) or above) +// Add Basic NNTP Interface +// Fix possible loop in lzhuf encode + +// Version 1.0.3.5 + +// Fix forwarding of Held Messages +// More attempts to fix Chat crashes. +// Limit join/leave problem with mismatched nodes. +// Add Chat Node Monitoring System. +// Change order of elements in nntp addresses (now to.at, was at.to) + +// Version 1.0.3.6 + +// Restart and Exit if too many errors +// Fix forwarding of killed messages. +// Fix Forwarding to PaKet. +// Fix problem if BBS signon contains words from the "Fail" list + +// Version 1.0.3.7 + +// re-fix loop if compressed size is greater than 32K - reintroduced in 1.0.3.4 +// Add last message to edit users +// Change Console and Monitor Buffer sizes +// Don't flag msg as 'Y' on read if it was Held or Killed + +// Version 1.0.3.8 + +// Don't connect if all messages for a BBS are held. +// Hold message if From or To are missing. +// Fix parsing of /n and /q commands +// fix possible loop on changing name or qth + +// Version 1.0.3.9 + +// More Chat fixes and monitoring +// Added additional console for chat + +// Version 1.0.3.10 + +// Fix for corruption of CIrcuit-Node chain. + +// Version 1.0.3.11 + +// Fix flow control for SMTP and NNTP + +// Version 1.0.3.12 + +// Fix crash in SendChatStatus if no Chat Links Defined. +// Disable Chat Mode if there is no ApplCall for ChatApplNum, +// Add Edit Message to Manage Messages Dialog +// NNTP needs authentication + + +// Version 1.0.3.13 + +// Fix Chat ApplCall warning when ChatAppl = 0 +// Add NNTP NEWGROUPS Command +// Fix MBL Forwarding (remove extra > prompt after SP) + +// Version 1.0.3.14 + +// Fix topic switch code. +// Send SYSOP messages on POP3 interface if User SYSOP flag is set. +// NNTP only needs Authentication for posting, not reading. + +// Version 1.0.3.15 + +// Fix reset of First to Forward after househeeping + +// Version 1.0.3.16 + +// Fix check of HA for terminating WW +// MBL Mode remove extra > prompts +// Fix program error if WP record has unexpected format +// Connect Script changes for WINMOR +// Fix typo in unconfigured node has connected message + +// Version 1.0.3.17 + +// Fix forwarding of Personals + +// Version 1.0.3.18 + +// Fix detection of misconfigured nodes to work with new nodes. +// Limit connection attempt rate when a chat node is unavailable. +// Fix Program Error on long input lines (> ~250 chars). + +// Version 1.0.3.19 + +// Fix Restart of B2 mode transfers. +// Fix error if other end offers B1 and you are configured for B2 only. + + +// Version 1.0.3.20 + +// Fix Paging in Chat Mode. +// Report Node Versions. + +// Version 1.0.3.21 + +// Check node is not already known when processing OK +// Add option to suppress emailing of housekeeping results + +// Version 1.0.3.22 + +// Correct Version processing when user connects via the network +// Add time controlled forwarding scripts + +// Version 1.0.3.23 + +// Changes to RMS forwarding + +// Version 1.0.3.24 + +// Fix RMS: from SMTP interface +// Accept RMS/ instead of RMS: for Thunderbird + +// Version 1.0.3.25 + +// Accept smtp: addresses from smtp client, and route to ISP gateway. +// Set FROM address of messages from RMS that are delivered to smtp client so a reply will go back via RMS. + +// Version 1.0.3.26 + +// Improve display of rms and smtp messages in message lists and message display. + +// Version 1.0.3.27 + +// Correct code that prevents mail being retured to originating BBS. +// Tidy stuck Nodes and Topics when all links close +// Fix B2 handling of @ to TO Address. + +// Version 1.0.3.28 + +// Ensure user Record for the BBS Call has BBS bit set. +// Don't send messages addressed @winlink.org if addressee is a local user with Poll RMS set. +// Add user configurable welcome messages. + +// Version 1.0.3.29 + +// Add AUTH feature to Rig Control + +// Version 1.0.3.30 + +// Process Paclink Header (;FW:) + +// Version 1.0.3.31 + +// Process Messages with attachments. +// Add inactivity timeout to Chat Console sessions. + +// Version 1.0.3.32 + +// Fix for Paclink > BBS Addresses + +// Version 1.0.3.33 + +// Fix multiple transfers per session for B2. +// Kill messages eent to paclink. +// Add option to forward messages on arrival. + +// Version 1.0.3.34 + +// Fix bbs addresses to winlink. +// Fix adding @winlink.org to imcoming paclink msgs + +// Version 1.0.3.35 + +// Fix bbs addresses to winlink. (Again) + +// Version 1.0.3.36 + +// Restart changes for RMS/paclink + +// Version 1.0.3.37 + +// Fix for RMS Express forwarding + +// Version 1.0.3.38 + +// Fixes for smtp and lower case packet addresses from Airmail +// Fix missing > afer NO - Bid in MBL mode + +// Version 1.0.3.39 + +// Use ;FW: for RMS polling. + +// Version 1.0.3.40 + +// Add ELSE Option to connect scripts. + +// Version 1.0.3.41 + +// Improved handling of Multiple Addresses +// Add user colours to chat. + +// Version 1.0.3.42 + +// Poll multiple SSID's for RMS +// Colour support for BPQTEerminal +// New /C chat command to toggle colour on or off. + +// Version 1.0.3.43 + +// Add SKIPPROMPT command to forward scripts + +// Version 1.0.4.1 + +// Non - Beta Release +// Fix possible crash/corruption with long B2 messages + +// Version 1.0.4.2 + +// Add @winlink.org to the B2 From addresss if it is just a callsign +// Route Flood Bulls on TO as well as @ + +// Version 1.0.4.3 + +// Handle Packet Addresses from RMS Express +// Fix for Housekeeping B$ messages + +// Version 1.0.4.4 + +// Remove B2 header and all but the Body part from messages forwared using MBL +// Fix handling of ;FW: from RMS Express + +// Version 1.0.4.5 + +// Disable Paging on forwarding sessions. +// Kill Msgs sent to RMS Exxpress +// Add Name to Chat *** Joined msg + +// Version 1.0.4.6 + +// Pass smtp:winlink.org messages from Airmail to local user check +// Only apply local user check to RMS: messages @winlink.org +// Check locally input smtp: messages for local winlink.org users +// Provide facility to allow only one connect on a port + +// Version 1.0.4.8 + +// Only reset last listed on L or LR commands. + +// Version 1.0.4.9 + +// Fix error in handling smtp: messages to winlink.org addresses from Airmail + +// Version 1.0.4.10 + +// Fix Badwords processing +// Add Connect Script PAUSE command + +// Version 1.0.4.11 + +// Suppress display and listing of held messages +// Add option to exclude SYSOP messages from LM, KM, etc +// Fix crash whan receiving messages with long lines via plain text forwarding + +// Version 1.0.4.12 Jul 2010 + +// Route P messages on AT +// Allow Applications above 8 + +// Version 1.0.4.13 Aug 2010 + +// Fix TidyString for addresses of form John Wiseman +// Add Try/Except around socket routines + +// Version 1.0.4.14 Aug 2010 + +// Trap "Error - TNC Not Ready" in forward script response +// Fix restart after program error +// Add INFO command +// Add SYSOP-configurable HELP Text. + +// Version 1.0.4.15 Aug 2010 + +// Semaphore Connect/Disconnect +// Semaphore RemoveTempBIDS + +// Version 1.0.4.16 Aug 2010 + +// Remove prompt after receiving unrecognised line in MBL mode. (for MSYS) + +// Version 1.0.4.17 Aug 2010 + +// Fix receiving multiple messages in FBB Uncompressed Mode +// Try to trap phantom chat node connections +// Add delay to close + + +// Version 1.0.4.18 Aug 2010 + +// Add "Send SYSTEM messages to SYSOP Call" Option +// set fwd bit on local winlink.org msgs if user is a BBS +// add winlink.org to from address of messages from WL2K that don't already have an @ + +// Version 1.0.4.19 Sept 2010 + +// Build a B2 From: address if possible, so RMS Express can reply to packet messages. +// Fix handling of addresses from WL2K with SSID's +// L@ now only matches up to length of input string. +// Remove "Type H for help" from login prompt. + +// Version 1.0.4.20 Sept 2010 + +// Process FBB 'E' response +// Handle FROM addresses with an @BBS +// Fix FROM addresses with @ on end. +// Extend delay before close after sending FQ on winmor/pactor sessions. + +// Version 1.0.4.21 Sept 2010 + +// Fix handling B2 From: with an HA +// Add "Expert User" welcome message. + +// Version 1.0.4.22 Sept 2010 + +// Version 1.0.4.23 Oct 2010 + +// Add Dup message supression +// Dont change B2 from if going to RMS + +// Version 1.0.4.24 Oct 2010 + +// Add "Save Registry Config" command +// Add forwarding on wildcarded TO for NTS +// Add option to force text mode forwarding +// Define new users as a temporaty BBS if SID received in reply to Name prompt +// Reduce delay before sending close after sending FQ on pactor sessions +// Fix processing of MIME boundary from GMail + +// Send /ex instead of ctrl/z for text mode forwarding +// Send [WL2K-BPQ... SID if user flagged as RMS Express +// Fix Chat Map reporting when more than one AXIP port +// Add Message State D for NTS Messages +// Forward messages in priority order - T, P, B +// Add Reject and Hold Filters +// Fix holding messages to local RMS users when received as part of a multiple addressee message + +// Version 1.0.4.25 Nov 2010 + +// Renumbered for release +// Add option to save Registry Config during Housekeeping + +// Version 1.0.4.26 Nov 2010 + +// Fix F> loop when doing MBL forwarding between BPQ BBSes +// Allow multiple To: addresses, separated by ; +// Allow Houskeeping Lifetime Overrides to apply to Unsent Messages. +// Set Unforwarded Bulls to status '$' +// Accept MARS and USA as continent codes for MARS Packet Addresses +// Add option to send Non-delivery notifications. + +// Version 1.0.4.27 Dec 2010 + +// Add MSGTYPES fwd file option + +// Version 1.0.4.28 Dec 2010 + +// Renumbered to for release + +// Version 1.0.4.30 Dec 2010 + +// Fix rescan requeuing where bull was rejected by a BBS +// Fiz flagging bulls received by NNTP with $ if they need to be forwarded. +// Add Chat Keepalive option. +// Fix bug in non-delivery notification. + +// Version 1.0.4.32 Jan 2011 + +// Allow "Send from Clipboard" to send to rms: or smtp: +// Allow messages received via SMTP to be bulls (TO preceeded by bull/) or NTS (to nnnnn@NTSXX or nnnnn@NTSXX.NTS) +// Fix corruption of messages converted to B2 if body contains binary data +// Fix occasional program error when forwarding B2 messages +// Limit FBB protocol data blocks to 250 to try to fix restart problem. +// Add F2 to F5 to open windows. + +// Version 1.0.4.33 Jan 2011 + +// Fix holding old bulls with forwarding info. + +// Version 1.0.4.33 Jan 2011 + +// Prevent transfer restarting after a program error. +// Allow Housekeeping to kill held messages. + +// Version 1.0.4.35 Jan 2011 + +// Add Size limits for P and T messages to MSGTYPES command +// Fix Error in MBL processing when blank lines received (introduced in .33) +// Trap possible PE in Send_MON_Datagram +// Don't use paging on chat sessions + +// Version 1.0.4.36 Jan 2011 + +// Fix error after handling first FBB block. +// Add $X and $x welcome message options. + +// Version 1.0.4.37 Jan 2011 + +// Change L command not to list the last message if no new ones are available +// Add LC I I@ IH IZ commands +// Add option to send warning to sysop if forwarded P or T message has nowhere to go +// Fixes for Winpack Compressed Download +// Fix Houskeeping when "Apply Overrides to Unsent Bulls" is set. +// Add console copy/paste. +// Add "No Bulls" Option. +// Add "Mail For" Beacon. +// Tidied up Tab order in config dialogs to help text-to-speech programs. +// Limit MaxMsgno to 99000. + +// Version 1.0.4.38 Feb 2011 + +// Renumbered for release + +// Version 1.0.4.40 April 2011 + +// Add POLLRMS command + +// Changes for Vista/Win7 (registry key change) +// Workaround for changes to RMS Express +// Fix AUTH bug in SMTP server +// Add filter to Edit Messages dialog + +// Version 1.0.4.41 April 2011 + +// Extend B2 proposals to other BPQMail systems so Reject Filter will work. +// Add Edit User Command +// Use internal Registry Save routine instead of Regedit +// Fix Start Forward/All +// Allow Winpack Compressed Upload/Download if PMS flag set (as well as BBS flag) +// Add FWD SYSOP command +// Fix security on POLLRMS command +// Add AUTH command +// Leave selection in same place after Delete User +// Combine SMTP server messages to multiple WL2K addresses into one message to WL2k +// Add option to show name as well as call on Chat messages +// Fix program error if you try to define more than 80 BBS's + +// Version 1.0.4.45 October 2011 + +// Changes to program error reporting. +// BBS "Returh to Node" command added +// Move config to "Standard" location (BPQ Directory/BPQMailChat) . +// Fix crash if "Edit Message" clicked with no message selected. + +// Version 1.0.4.46 October 2011 + +// Fix BaseDir test when BaseDir ends with \ or / +// Fix long BaseDir values (>50 chars) + +// Version 1.4.47.1 January 2012 + +// Call CloseBPQ32 on exit +// Add option to flash window instead of sounding bell on Chat Connects +// Add ShowRMS SYSOP command +// Update WP with I records from R: lines +// Send WP Updates +// Fix Paclen on Pactor-like sessions +// Fix SID and Prompt when RMS Express User is set +// Try to stop loop in Program Error/Restarting code +// Trap "UNABLE TO CONNECT" response in connect script +// Add facility to print messages or save them to a text file + +// Version 1.4.48.1 January 2012 + +// Add Send Message (as well as Send from Clipboard) +// Fix Email From: Address when forwaring using B2 +// Send WP from BBSCALL not SYSOPCALL +// Send Chat Map reports via BPQ32.dll + + +// Version 1.4.49.1 February 2012 + + +// Fix Setting Paclink mode on SNOS connects +// Remove creation of debugging file for each message +// Add Message Export and Import functions +// All printing of more than one message at a time +// Add command to toggle "Expert" status + +// Version 1.4.50.1 February 2012 + +// Fix forwarding to RMS Express users +// Route messages received via B2 to an Internet email address to RMS +// Add Reverse Poll interval +// Add full FROM address to POP3 messages +// Include HOMEBBS command in Help + + +// Version 1.4.51.1 June 2012 + +// Allow bulls to be sent from RMS Express. +// Handle BASE64 and Quoted-printable encoding of single part messages +// Work round for RMS Express "All proposals rejected" Bug. + +// Version 1.4.52.1 August 2012 + +// Fix size limit on B2 To List when sending to multiple dests +// Fix initialisation of DIRMES.SYS control record +// Allow use of Tracker and UZ7HO ports for UI messages + +// Version 1.4.53.1 September 2012 + +// Fix crash if R: line with out a CR found. + +// Version 1.4.54.1 ?? 2012 + +// Add configurable prompts +// Fix KISS-Only Test +// Send EHLO instead of HELO when Authentication is needed on SMTP session +// Add option to use local tome for bbs forwarding config +// Allow comment lines (; or @) or single space in fwd scripts +// Fix loss of forwarding info if SAVE is clicked before selecting a call + +// Version 1.4.55.1 June 2013 + +// Add option to remove users that have not connected for a long time. +// Add l@ smtp: +// Fix From: sent to POP3 Client when meaages is from RMS +// Display Email From on Manage Messages + +// Version 1.4.56.1 July 2013 + +// Add timeout +// Verify prompts +// Add IDLETIME command + + + +// Version 1.4.57.1 + +// Change default IDLETIME +// Fix display of BBS's in Web "Manage Messages" +// Add separate househeeping lifetines for T messages +// Don't change flag on forwarded or delivered messages if they sre subsequently read +// Speed up processing, mainly to stop RMS Express timing out when connecting via Telnet +// Don't append winlink.org to RMS Express or Paclink addresses if RMS is not configured +// Fix receiving NTS messages via B2 +// Add option to send "Mail For", but not FBB Headers +// Fix corruption caused with Subject longer than 60 bytes reveived from Winlink systems +// Fix Endian bug in FBB Compression code + + +// Version 1.4.58.1 + +// Change control of appending winlink.org to RMS Express or Paclink addresses to a user flag +// Lookup HomeBBS and WP for calls without a via received from RMS Express or Paclink +// Treat call@bpq as request to look up address in Home BBS/WP for messages received from RMS Express or Paclink +// Collect stats by message type +// Fix Non-Delivery notifications to SMTP messages +// Add Message Type Stats to BBS Trafic Report +// Add "Batch forward to email" +// Add EXPORT command +// Allow more BBS records +// Allow lower case connect scripts +// Fix POP3 LIST command +// Fix MIME Multipart Alternate with first part Base64 or Quoted Printable encoding +// Fix duplicates of SP SYSOP@WW Messages +// Add command line option (tidymail) to delete redundant Mail files +// Add command line option (nohomebbs) to suppress HomeBBS prompt + +// 59 April 2014 + +// Add FLARQ Mail Mode +// Fix possible crash saving restart data +// Add script command ADDLF for connect scripts over Telnet +// Add recogniton of URONODE connected message +// Add option to stop Name prompt +// Add new RMS Express users with "RMS Express User" flag set +// Validate HTML Pages +// Add NTS swap file +// Add basic File list and read functions +// Fix Traffic report + +// 60 + +// Fix security hole in readfile + +// 61 August 2014 +// Set Messages to NTS:nnnnn@NTSXX to type 'T' and remove NTS +// Dont treat "Attempting downlink" as a failure +// Add option to read messages during a list +// Fix crash during message renumber on MAC +// Timeout response to SID to try to avoid hang on an incomplete connection. +// Save config in file instead of registry +// Fix Manage Messages "EXPORT" option and check filename on EXPORT command +// Fix reverse forward prompt in MBL mode. +// Fix From address in POP3 messages where path is @winlink.org +// Fix possible program error in T message procesing +// Add MaxAge param (for incoming Bulls) + + +//62 November 2014 +// Add ZIP and Permit Bulls flag to Manage Users +// Allow users to kill their own B and anyone to kill T messages +// Improve saving of "Last Listed" +// Fix LL when paging +// Send Date received in R: Line (should fix B2 message restarts) +// Fix occasional crash in terminal part line processing +// Add "SKIPCON" forwarding command to handle nodes that include "Connected" in their CTEXT +// Fix possible retry loop when message is deferred (FBB '=' response); +// Don't remove Attachments from received bulls. + +//63 Feb 2015 + +// Fix creating Bulls from RMS Express messages. +// Fix PE if message with no To: received. +// Fix setting "RMS Express User" flag on new connects from RMS Express +// Fix deleting 'T' messages downloaded by RMS Express +// Include MPS messages in count of messages to forward. +// Add new Welcome Message variable $F for messages to forward +// Fix setting Type in B2 header when usong NTS: or BULL: +// Remove trailing spaces from BID when Creating Message from Clipboard. +// Improved handling of FBB B1/B2 Restarts. + +//64 September 2015 + +// Fix Message Type in msgs from RMS Express to Internet +// Reopen Monitor window if open when program list closed +// Only apply NTS alias file to NTS Messages +// Fix failure to store some encrypted ISP passwords +// Allow EDITUSER to change "RMS Express User" flag +// Fix reporting of Config File errors +// Fix Finding MPS Messages (First to Forward was being used incorrectly) +// Add "Save Attachment" to Web Mgmt Interface +// Support Secure Signon on Forwarding sessions to CMS +// Save Forwarding config when BBS flag on user is cleared +// Pass internally generated SYSOP messages through routing process +// Add POP3 TOP command. +// Don't set 'T' messages to 'Y' when read. +// Add optional temporary connect script on "FWD NOW" command +// Add automatic import facility +// Accept RMS mail to BBS Call even if "Poll RMS" not set. + +// 65 November 2015 + +// Fix loading Housekeeping value for forwarded bulls. +// Fix re-using Fwd script override in timer driven forwarding. +// Add ampr.org handling +// Add "Dont forward" match on TO address for NTS +// Allow listing a combinatiom of state and type, such as LNT or LPF +// Fix handling ISP messages from gmail without a '+' +// Add basic WebMail support + +// 66 + +// Autoimport messages as Dummy Call, not SYSOP Call +// Add "My Messages" display option to WebMail +// Create .csv extract of User List during hourekeeping. +// Fix processing of NTS Alising of @ Addresses +// Don't reroute Delivered NTS Messages +// Add option to stop users killing T messages +// Add multicast Receive +// Fix initialising new message database format field +// Fix "Forward Messages to BBS Call" option. +// Add Filter WP Bulls option and allow multiple WP "TO" addresses +// Fix deleting P WP messages for other stations +// Fix saving blank lines in forwarding config +// Fix paging on L@ and l< +// Fix removing DELETE from IMPORT XXX DELETE and allow multiple IMPORT lines in script +// Run DeleteRedundantMessages before renumbering messages +// Connect script now tries ELSE lines if prompt not received from remote BBS +// Send connecting call instead of BBS Name when connecting to CMS server. +// Add BID filter to Manage Messages +// Fix handling of over long suject lines in IMPORT +// Allow comments before ELSE in connect script +// Add Copy and Clear to Multicast Window +// Fix possible duplicate messages with MBL forwarding +// Set "Permit EMail" on IMPORT dummy User. +// Fix repeated running of housekeeping if clock is stepped forward. +// Fix corruption of CMS Pass field by Web interface +// Kill B2 WP bulls if FilterWPBulls set +// Include Message Type in BPQ B2 proposal extensions + +// 6.0.14.1 July 2017 + +// Fix corruption of BBSNumber if RMS Ex User and BBS both checked +// Tread B messages without an AT as Flood. +// Make sure Message headers are always saved to disk when a message status changes +// Reject message instead of failing session if TO address too long in FBB forwarding +// Fix error when FBB restart data exactly fills a packet. +// Fix possible generation of msg number zero in send nondlivery notification +// Fix problem with Web "Manage Messages" when stray message number zero appears +// Fix Crash in AMPR forward when host missing from VIA +// Fix possible addition of an spurious password entry to the ;FW: line when connecting to CMS +// Fix test for Status "D" in forward check. +// Don't cancel AUTH on SMTP RSET +// Fix "nowhere to go" message on some messages sent to smtp addresses +// Add @ from Home BBS or WP is not spcified in "Send from Clipboard" + +// 6.0.15.1 Feb 2018 + +// Fix PE if Filename missing from FILE connect script command +// Suppress reporting errors after receiving FQ +// Fix problem caused by trailing spaces on callsign in WP database +// Support mixed case WINLINK Passwords + +// 6.0.16.1 March 2018 + +// Make sure messages sent to WL2K don;'t have @ on from: address +// If message to saildocs add R: line as an X header instead of to body +// Close session if more than 4 Invalid Commmad responses sent +// Report TOP in POP3 CAPA list. Allows POP3 to work with Windows Mail client + +// 6.0.17.1 November 2018 + +// Add source routing using ! eg sp g8bpq@winlink.org!gm8bpq to send via RMS on gm8bpq +// Accept an internet email address without rms: or smtp: +// Fix "Forward messages for BBS Call" when TO isn't BBS Call +// Accept NNTP commands in either case +// Add NNTP BODY command +// Timeout POP or SMTP TCP connections that are open too long +// Add YAPP support +// Fix connect script when Node CTEXT contains "} BBS " +// Fix handling null H Route +// Detect and correct duplicate BBS Numbers +// Fix problem if BBS requests FBB blocked forwarding without compression (ie SID of F without B) +// Fix crash if YAPP entered without filenmame and send BBS prompt after YAPP error messages +// Add support for Winlink HTML Forms to WebMail interface +// Update B2 header when using NTS alias file with B2 messages + +// 6.0.18.1 January 2019 + +// Ensure callsigns in WP database are upper case. +// Various fixes for Webmail +// Fix sending direct to ampr.org addresses +// Use SYSOP Call as default for Webmail if set +// Preparations for 64 bit version + + +// 6.0.19.1 September 2019 + +// Trap missing HTML reply Template or HTML files +// Fix case problems in HTML Templates +// Fix setting To call on reply to HTML messages +// More preparations for 64 bit including saving WP info as a text file. +// Set "RMS Express User" when a new user connects using PAT +// Increace maximum length on Forwarding Alias string in Web interface +// Expand multiaddress messages from Winlink Express if "Don't add @Winlink.org" set or no RMS BBS +// Fix program error if READ used without a filename +// Trap reject messages from Winlink CMS +// Fix "delete to recycle bin" on Linux +// Handle Radio Only Messages (-T or -R suffix on calling station) +// Fix program error on saving empty Alias list on Web Forwarding page +// Add REQDIR and REQFIL +// Experimental Blocked Uncompressed forwarding +// Security fix for YAPP +// Fix WebMail Cancel Send Message +// Fix processing Hold Message response from Winlink Express + +// 6.0.20.1 April 2020 + +// Improvments to YAPP +// Add Copy forwarding config +// Add Next and Previous buttons to Webmail message read screen +// Move HTML templates from HTMLPages to inline code. +// Fix Paclen on YAPP send +// Fix bug in handling "RMS Express User" +// Fix WINPACK compressed forwarding +// Add option to send P messages to more than one BBS +// Add "Default to Don't Add WINLINK.ORG" Config option +// Re-read Badwords.sys during Housekeeping +// Add BID Hold and Reject Filters +// On SMTP Send try HELO if EHLO rejected +// Allow SID response timeout to be configured per BBS +// Fix sending bulls with PAT +// Set "Forward Messages to BBS Call" when routing Bulls on TO +// Add option to send Mail For Message to APRS +// Fix WP update +// Fix Holding messages from Webmail Interface +// Add RMR command +// Add REROUTEMSGS BBS SYSOP command +// Disable null passwords and check Exclude flag in Webmail Signin +// Add basic Webmail logging + +// 6.0.21.1 December 2020 + +// Remove nulls from displayed messages. +// Fix Holding messages from SMTP and POP3 Interfaces +// Various fixes for handling messages to/from Internet email addresses +// Fix saving Email From field in Manage Messages +// Fix sending WL2K traffic reports via TriMode. +// Fix removing successive CR from Webmail Message display +// Fix Wildcarded @ forwarding +// Fix message type when receiving NTS Msgs form Airmail +// Fix address on SERVICE messages from Winlink +// Add multiple TO processing to Webmail non-template messages +// Don't backup config file if reading it fails +// Include Port and Freq on Connected log record +// Make sure welcome mesages don't end in > +// Allow flagging unread T messages as Delivered +// Replace \ with # in forward script so commands starting with # can be sent +// Fix forwarding NTS on TO field +// Fix possible crash in text mode forwarding +// Allow decimals of days in P message lifetimes and allow Houskeeping interval to be configured +// Add DOHOUSEKEEPING sysop command +// Add MARS continent code +// Try to trap 'zombie' BBS Sessions +// On Linux if "Delete to Recycle Bin" is set move deleted messages and logs to directory Deleted under current directory. +// Fix corruption of message length when reading R2 message via Read command +// Fix paging on List command and add new combinations of List options +// Fix NNTP list and LC command when bulls are killed + +// 6.0.22.1 August 2021 + +// Fix flagging messages with attachments as read. +// Fix possible corruption of WP database and subsequent crash on reloading. +// Fix format of Web Manage Messages display +// Include SETNEXTMESSAGENUMBER in SYSOP Help Message +// Fix occasional "Incoming Connect from SWITCH" +// Fix L> with numeric dests +// Improved diagnostic for MailTCP select() error. +// Clear "RMS Express User" if user is changed to a BBS +// Fix saving Window positions on exit +// Fix parsing ReplyTemplate name in Webmail +// Handle multiple addressees for WebMail Forms messages to packet stations +// Add option to allow only known users to connect +// Add basic callsign validation to From address +// Add option to forward a user's messages to Winlink +// Move User config to main config file. +// Update message status whne reading a Forms Webmail message +// Speed up killing multiple messages +// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command + +// 6.0.23.1 June 2022 + +// Fix crash when ; added to call in send commands +// Allow smtp/ override for messages from RMS Express to send via ISP gateway +// Send Internet email from RMS Express to ISP Gateway if enabled and RMS BBS not configured +// Recompiled for Web Interface changes in Node +// Add RMS Relay SYNC Mode (.17) +// Add Protocol changes for Relay RO forwarding +// Add SendWL2KPM command to connect script to allow users other than RMS to send ;FW: string to RMS Relay +// Fix B2 Header Date in Webmail message with sttachments. +// Fix bug when using YAPP with VARA (.27) +// Allow SendWL2KFW as well as the (incorrect)SendWL2KPM command +// Add mechsnism to send bbs log records to qttermtcp. (32) +// Add MFJ forwarding Mode (No @BBS on send) +// Fix handling CR/LF split over packet boundaries +// Add Header and Footers for Webmail Send (42) +// Fix Maintenance Interval in LinBPQ (53) +// Add RMS: to valid from addresses (.56) +// Fix Web management on Android deviced (.58) +// Disconnect immediately if "Invalid Command" "*** Protocol Error" or "Already Connected" received (.70) +// Check Badword and Reject filters before processing WP Messages + +// 6.0.24.1 ?? 2022 + +// Fix ' in Webmail subject (8) +// Change web buttons to white on black when pressed (10) +// Add auto-refresh option to Webmail index page (25) +// Fix displaying help and info files with crlf line endings on Linux (28) +// Improve validation of extended FC message (32) +// Improve WP check for SYSTEM as a callsign (33) +// Improvements to RMS Relay SYNC mode (47) +// Fix BID Hold and Reject filters +// Fix Webmail auto-refresh when page exceeds 64K bytes (54) +// Fix Webmail send when using both headers/footers and attachmonts (55) +// Fix R: line corruption on some 64 bit builds +// Dont drop empty lines inm TEXTFORWARDING (61) +// Dont wait for body prompt for TEXTFORWARDING for SID [PMS-3.2-C$] (62) +// Add forwarding mode SETCALLTOSENDER for PMS Systems that don't accept < in SP (63) +// QtTerm Monitoring fixed for 63 port version of BPQ (69) + + +#include "bpqmail.h" +#include "winstdint.h" +#define MAIL +#include "Versions.h" + +#include "GetVersion.h" + +#define MAX_LOADSTRING 100 + +typedef int (WINAPI FAR *FARPROCX)(); +typedef int (WINAPI FAR *FARPROCZ)(); + +FARPROCX pDllBPQTRACE; +FARPROCZ pGetLOC; +FARPROCX pRefreshWebMailIndex; +FARPROCX pRunEventProgram; + +BOOL WINE = FALSE; + +INT_PTR CALLBACK UserEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK MsgEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK FwdEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +VOID SetupNTSAliases(char * FN); + +HKEY REGTREE = HKEY_LOCAL_MACHINE; // Default +char * REGTREETEXT = "HKEY_LOCAL_MACHINE"; + +// Global Variables: +HINSTANCE hInst; // current instance +TCHAR szTitle[MAX_LOADSTRING]; // The title bar text +TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +UINT BPQMsg; + +HWND MainWnd; +HWND hWndSess; +RECT MainRect; +HMENU hActionMenu; +static HMENU hMenu; +HMENU hDisMenu; // Disconnect Menu Handle +HMENU hFWDMenu; // Forward Menu Handle + +int SessX, SessY, SessWidth; // Params for Session Window + +char szBuff[80]; + +#define MaxSockets 64 + +int _MYTIMEZONE = 0; + +ConnectionInfo Connections[MaxSockets+1]; + +//struct SEM AllocSemaphore = {0, 0}; +//struct SEM ConSemaphore = {0, 0}; +//struct SEM OutputSEM = {0, 0}; + +//struct UserInfo ** UserRecPtr=NULL; +//int NumberofUsers=0; + +//struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes + +//struct MsgInfo ** MsgHddrPtr=NULL; +//int NumberofMessages=0; + +//int FirstMessageIndextoForward=0; // Lowest Message wirh a forward bit set - limits search + +//BIDRec ** BIDRecPtr=NULL; +//int NumberofBIDs=0; + +extern BIDRec ** TempBIDRecPtr; +//int NumberofTempBIDs=0; + +//WPRec ** WPRecPtr=NULL; +//int NumberofWPrecs=0; + +extern char ** BadWords; +//int NumberofBadWords=0; +extern char * BadFile; + +//int LatestMsg = 0; +//struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg +//int HighestBBSNumber = 0; + +//int MaxMsgno = 60000; +//int BidLifetime = 60; +//int MaintInterval = 24; +//int MaintTime = 0; +//int UserLifetime = 0; + + +BOOL cfgMinToTray; + +BOOL DisconnectOnClose; + +extern char PasswordMsg[100]; + +char cfgHOSTPROMPT[100]; + +char cfgCTEXT[100]; + +char cfgLOCALECHO[100]; + +char AttemptsMsg[]; +char disMsg[]; + +char LoginMsg[]; + +char BlankCall[]; + + +ULONG BBSApplMask; +ULONG ChatApplMask; + +int BBSApplNum; + +//int StartStream=0; +int NumberofStreams; +int MaxStreams; + +extern char BBSSID[]; +extern char ChatSID[]; + +extern char NewUserPrompt[100]; + +extern char * WelcomeMsg; +extern char * NewWelcomeMsg; +extern char * ExpertWelcomeMsg; + +extern char * Prompt; +extern char * NewPrompt; +extern char * ExpertPrompt; + +extern BOOL DontNeedHomeBBS; + +char BBSName[100]; +char MailForText[100]; + +char SignoffMsg[100]; + +char AbortedMsg[100]; + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +char NTSAliasesPath[MAX_PATH]; +extern char NTSAliasesName[MAX_PATH]; + +char BaseDir[MAX_PATH]; +char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% + +char MailDir[MAX_PATH]; + +char RlineVer[50]; + +extern BOOL KISSOnly; + +extern BOOL OpenMon; + +extern struct ALIAS ** NTSAliases; + +extern int EnableUI; +extern int RefuseBulls; +extern int SendSYStoSYSOPCall; +extern int SendBBStoSYSOPCall; +extern int DontHoldNewUsers; +extern int ForwardToMe; + +extern int MailForInterval; + +char zeros[NBMASK]; // For forward bitmask tests + +time_t MaintClock; // Time to run housekeeping + +struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List. + +// Filter Params + +char ** RejFrom; // Reject on FROM Call +char ** RejTo; // Reject on TO Call +char ** RejAt; // Reject on AT Call +char ** RejBID; // Reject on BID + +char ** HoldFrom; // Hold on FROM Call +char ** HoldTo; // Hold on TO Call +char ** HoldAt; // Hold on AT Call +char ** HoldBID; // Hold on BID + + +// Send WP Params + +BOOL SendWP; +char SendWPVIA[81]; +char SendWPTO[11]; +int SendWPType; + + +int ProgramErrors = 0; + +UCHAR BPQDirectory[260] = ""; + + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +ATOM RegisterMainWindowClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +unsigned long _beginthread( void( *start_address )(VOID * DParam), + unsigned stack_size, VOID * DParam); + +VOID SendMailForThread(VOID * Param); +BOOL CreatePipeThread(); +int DeleteRedundantMessages(); +VOID BBSSlowTimer(); +VOID CopyConfigFile(char * ConfigName); +BOOL CreateMulticastConsole(); +char * CheckToAddress(CIRCUIT * conn, char * Addr); +BOOL CheckifPacket(char * Via); +int GetHTMLForms(); + +struct _EXCEPTION_POINTERS exinfox; + +CONTEXT ContextRecord; +EXCEPTION_RECORD ExceptionRecord; + +DWORD Stack[16]; + +BOOL Restarting = FALSE; + +Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg) +{ + unsigned int SPPtr; + unsigned int SPVal; + + memcpy(&ContextRecord, exinfo->ContextRecord, sizeof(ContextRecord)); + memcpy(&ExceptionRecord, exinfo->ExceptionRecord, sizeof(ExceptionRecord)); + + SPPtr = ContextRecord.Esp; + + Debugprintf("BPQMail *** Program Error %x at %x in %s", + ExceptionRecord.ExceptionCode, ExceptionRecord.ExceptionAddress, Msg); + + + __asm{ + + mov eax, SPPtr + mov SPVal,eax + lea edi,Stack + mov esi,eax + mov ecx,64 + rep movsb + + } + + Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", + ContextRecord.Eax, ContextRecord.Ebx, ContextRecord.Ecx, + ContextRecord.Edx, ContextRecord.Esi, ContextRecord.Edi, SPVal); + + Debugprintf("Stack:"); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); + +} + + + +void myInvalidParameterHandler(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t pReserved) +{ + Logprintf(LOG_DEBUG_X, NULL, '!', "*** Error **** C Run Time Invalid Parameter Handler Called"); + + if (expression && function && file) + { + Logprintf(LOG_DEBUG_X, NULL, '!', "Expression = %S", expression); + Logprintf(LOG_DEBUG_X, NULL, '!', "Function %S", function); + Logprintf(LOG_DEBUG_X, NULL, '!', "File %S Line %d", file, line); + } +} + +// If program gets too many program errors, it will restart itself and shut down + +VOID CheckProgramErrors() +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char ProgName[256]; + + if (Restarting) + exit(0); // Make sure can't loop in restarting + + ProgramErrors++; + + if (ProgramErrors > 25) + { + Restarting = TRUE; + + Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); + + if (cfgMinToTray) + { + DeleteTrayMenuItem(MainWnd); + if (ConsHeader[0]->hConsole) + DeleteTrayMenuItem(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DeleteTrayMenuItem(ConsHeader[1]->hConsole); + if (hMonitor) + DeleteTrayMenuItem(hMonitor); + } + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + GetModuleFileName(NULL, ProgName, 256); + + Debugprintf("Attempting to Restart %s", ProgName); + + CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); + + exit(0); + } +} + + +VOID WriteMiniDump() +{ +#ifdef WIN32 + + HANDLE hFile; + BOOL ret; + char FN[256]; + + sprintf(FN, "%s/Logs/MiniDump%x.dmp", GetBPQDirectory(), time(NULL)); + + hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + // Create the minidump + + ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + hFile, MiniDumpNormal, 0, 0, 0 ); + + if(!ret) + Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError()); + else + Debugprintf("Minidump %s created.", FN); + CloseHandle(hFile); + } +#endif +} + + +void GetSemaphore(struct SEM * Semaphore, int ID) +{ + // + // Wait for it to be free + // +#ifdef WIN32 + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; + } +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // + // try to get semaphore + // + + _asm{ + + mov eax,1 + mov ebx, Semaphore + xchg [ebx],eax // this instruction is locked + + cmp eax,0 + jne loop1 // someone else got it - try again +; +; ok, weve got the semaphore +; + } +#else + + while (Semaphore->Flag) + usleep(10000); + + Semaphore->Flag = 1; + +#endif + return; +} + +void FreeSemaphore(struct SEM * Semaphore) +{ + Semaphore->Flag = 0; + + return; +} + +char * CmdLine; + +extern int configSaved; + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + MSG msg; + HACCEL hAccelTable; + int BPQStream, n; + struct UserInfo * user; + struct _EXCEPTION_POINTERS exinfo; + _invalid_parameter_handler oldHandler, newHandler; + char Msg[100]; + int i = 60; + struct NNTPRec * NNTPREC; + struct NNTPRec * SaveNNTPREC; + + CmdLine = _strdup(lpCmdLine); + _strlwr(CmdLine); + + if (_stricmp(lpCmdLine, "Wait") == 0) // If AutoRestart then Delay 60 Secs + { + hWnd = CreateWindow("STATIC", "Mail Restarting after Failure - Please Wait", 0, + CW_USEDEFAULT, 100, 550, 70, + NULL, NULL, hInstance, NULL); + + ShowWindow(hWnd, nCmdShow); + + while (i-- > 0) + { + sprintf(Msg, "Mail Restarting after Failure - Please Wait %d secs.", i); + SetWindowText(hWnd, Msg); + + Sleep(1000); + } + + DestroyWindow(hWnd); + } + + __try { + + // Trap CRT Errors + + newHandler = myInvalidParameterHandler; + oldHandler = _set_invalid_parameter_handler(newHandler); + + // Initialize global strings + LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadString(hInstance, IDC_BPQMailChat, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_BPQMailChat)); + + // Main message loop: + + Logprintf(LOG_DEBUG_X, NULL, '!', "Program Starting"); + Logprintf(LOG_BBS, NULL, '!', "BPQMail Starting"); + Debugprintf("BPQMail Starting"); + + if (pDllBPQTRACE == 0) + Logprintf(LOG_BBS, NULL, '!', "Remote Monitor Log not available - update BPQ32.dll to enable"); + + + } My__except_Routine("Init"); + + while (GetMessage(&msg, NULL, 0, 0)) + { + __try + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + #define EXCEPTMSG "GetMessageLoop" + #include "StdExcept.c" + + CheckProgramErrors(); + } + } + + __try + { + for (n = 0; n < NumberofStreams; n++) + { + BPQStream=Connections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + + + hWnd = CreateWindow("STATIC", "Mail Closing - Please Wait", 0, + 150, 200, 350, 40, NULL, NULL, hInstance, NULL); + + ShowWindow(hWnd, nCmdShow); + + Sleep(1000); // A bit of time for links to close + + DestroyWindow(hWnd); + + if (ConsHeader[0]->hConsole) + DestroyWindow(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DestroyWindow(ConsHeader[1]->hConsole); + if (hMonitor) + { + DestroyWindow(hMonitor); + hMonitor = (HWND)1; // For status Save + } + + +// SaveUserDatabase(); + SaveMessageDatabase(); + SaveBIDDatabase(); + + configSaved = 1; + SaveConfig(ConfigName); + + if (cfgMinToTray) + { + DeleteTrayMenuItem(MainWnd); + if (ConsHeader[0]->hConsole) + DeleteTrayMenuItem(ConsHeader[0]->hConsole); + if (ConsHeader[1]->hConsole) + DeleteTrayMenuItem(ConsHeader[1]->hConsole); + if (hMonitor) + DeleteTrayMenuItem(hMonitor); + } + + // Free all allocated memory + + for (n = 0; n <= NumberofUsers; n++) + { + user = UserRecPtr[n]; + + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + free(user->Temp); + + free(user); + } + + free(UserRecPtr); + + for (n = 0; n <= NumberofMessages; n++) + free(MsgHddrPtr[n]); + + free(MsgHddrPtr); + + for (n = 0; n <= NumberofWPrecs; n++) + free(WPRecPtr[n]); + + free(WPRecPtr); + + for (n = 0; n <= NumberofBIDs; n++) + free(BIDRecPtr[n]); + + free(BIDRecPtr); + + if (TempBIDRecPtr) + free(TempBIDRecPtr); + + NNTPREC = FirstNNTPRec; + + while (NNTPREC) + { + SaveNNTPREC = NNTPREC->Next; + free(NNTPREC); + NNTPREC = SaveNNTPREC; + } + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + n = 0; + + if (Aliases) + { + while(Aliases[n]) + { + free(Aliases[n]->Dest); + free(Aliases[n]); + n++; + } + + free(Aliases); + FreeList(AliasText); + } + + n = 0; + + if (NTSAliases) + { + while(NTSAliases[n]) + { + free(NTSAliases[n]->Dest); + free(NTSAliases[n]); + n++; + } + + free(NTSAliases); + } + + FreeOverrides(); + + FreeList(RejFrom); + FreeList(RejTo); + FreeList(RejAt); + FreeList(RejBID); + FreeList(HoldFrom); + FreeList(HoldTo); + FreeList(HoldAt); + FreeList(HoldBID); + FreeList(SendWPAddrs); + + Free_UI(); + + for (n=1; n<20; n++) + { + if (MyElements[n]) free(MyElements[n]); + } + + free(WelcomeMsg); + free(NewWelcomeMsg); + free(ExpertWelcomeMsg); + + free(Prompt); + free(NewPrompt); + free(ExpertPrompt); + + FreeWebMailMallocs(); + + free(CmdLine); + + _CrtDumpMemoryLeaks(); + + } + My__except_Routine("Close Processing"); + + CloseBPQ32(); // Close Ext Drivers if last bpq32 process + + return (int) msg.wParam; +} + + + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +// COMMENTS: +// +// This function and its usage are only necessary if you want this code +// to be compatible with Win32 systems prior to the 'RegisterClassEx' +// function that was added to Windows 95. It is important to call this function +// so that the application will get 'well formed' small icons associated +// with it. +// +// +#define BGCOLOUR RGB(236,233,216) +//#define BGCOLOUR RGB(245,245,245) + +HBRUSH bgBrush; + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEX wcex; + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = DLGWINDOWEXTRA; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(BPQICON)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = bgBrush; + wcex.lpszMenuName = MAKEINTRESOURCE(IDC_BPQMailChat); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(BPQICON)); + + return RegisterClassEx(&wcex); +} + + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + +HWND hWnd; + +int AXIPPort = 0; + +char LOC[7] = ""; + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + char Title[80]; + WSADATA WsaData; + HMENU hTopMenu; // handle of menu + HKEY hKey=0; + int retCode; + RECT InitRect; + RECT SessRect; + struct _EXCEPTION_POINTERS exinfo; + + HMODULE ExtDriver = LoadLibrary("bpq32.dll"); + + if (ExtDriver) + { + pDllBPQTRACE = GetProcAddress(ExtDriver,"_DllBPQTRACE@8"); + pGetLOC = GetProcAddress(ExtDriver,"_GetLOC@0"); + pRefreshWebMailIndex = GetProcAddress(ExtDriver,"_RefreshWebMailIndex@0"); + pRunEventProgram = GetProcAddress(ExtDriver,"_RunEventProgram@8"); + + if (pGetLOC) + { + char * pLOC = (char *)pGetLOC(); + memcpy(LOC, pLOC, 6); + } + } + + // See if running under WINE + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + RegCloseKey(hKey); + WINE =TRUE; + Debugprintf("Running under WINE"); + } + + + REGTREE = GetRegistryKey(); + REGTREETEXT = GetRegistryKeyText(); + + Sleep(1000); + + { + int n; + struct _EXTPORTDATA * PORTVEC; + + KISSOnly = TRUE; + + for (n=1; n <= GetNumberofPorts(); n++) + { + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(n); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + { + if (_memicmp(PORTVEC->PORT_DLL_NAME, "TELNET", 6) == 0) + KISSOnly = FALSE; + + if (PORTVEC->PORTCONTROL.PROTOCOL != 10) // Pactor/WINMOR + KISSOnly = FALSE; + + if (AXIPPort == 0) + { + if (_memicmp(PORTVEC->PORT_DLL_NAME, "BPQAXIP", 7) == 0) + { + AXIPPort = PORTVEC->PORTCONTROL.PORTNUMBER; + KISSOnly = FALSE; + } + } + } + } + } + + hInst = hInstance; + + hWnd=CreateDialog(hInst,szWindowClass,0,NULL); + + if (!hWnd) + { + return FALSE; + } + + MainWnd = hWnd; + + GetVersionInfo(NULL); + + sprintf(Title,"G8BPQ Mail Server Version %s", VersionString); + + sprintf(RlineVer, "BPQ%s%d.%d.%d", (KISSOnly) ? "K" : "", Ver[0], Ver[1], Ver[2]); + + SetWindowText(hWnd,Title); + + hWndSess = GetDlgItem(hWnd, 100); + + GetWindowRect(hWnd, &InitRect); + GetWindowRect(hWndSess, &SessRect); + + SessX = SessRect.left - InitRect.left ; + SessY = SessRect.top -InitRect.top; + SessWidth = SessRect.right - SessRect.left; + + // Get handles for updating menu items + + hTopMenu=GetMenu(MainWnd); + hActionMenu=GetSubMenu(hTopMenu,0); + + hFWDMenu=GetSubMenu(hActionMenu,0); + hMenu=GetSubMenu(hActionMenu,1); + hDisMenu=GetSubMenu(hActionMenu,2); + + CheckTimer(); + + cfgMinToTray = GetMinimizetoTrayFlag(); + + if ((nCmdShow == SW_SHOWMINIMIZED) || (nCmdShow == SW_SHOWMINNOACTIVE)) + if (cfgMinToTray) + { + ShowWindow(hWnd, SW_HIDE); + } + else + { + ShowWindow(hWnd, nCmdShow); + } + else + ShowWindow(hWnd, nCmdShow); + + UpdateWindow(hWnd); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + __try { + + return Initialise(); + + }My__except_Routine("Initialise"); + + return FALSE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + int state,change; + ConnectionInfo * conn; + struct _EXCEPTION_POINTERS exinfo; + + + if (message == BPQMsg) + { + if (lParam & BPQMonitorAvail) + { + __try + { + DoBBSMonitorData(wParam); + } + My__except_Routine("DoMonitorData"); + + return 0; + + } + if (lParam & BPQDataAvail) + { + // Dont trap error at this level - let Node error handler pick it up +// __try +// { + DoReceivedData(wParam); +// } +// My__except_Routine("DoReceivedData") + return 0; + } + if (lParam & BPQStateChange) + { + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + __try + { + SessionState(wParam, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + __try {Connected(wParam);} + My__except_Routine("Connected"); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + __try{Disconnected(wParam);} + My__except_Routine("Disconnected"); + FreeSemaphore(&ConSemaphore); + } + } + } + My__except_Routine("DoStateChange"); + + } + + return 0; + } + + + switch (message) + { + + case WM_KEYUP: + + switch (wParam) + { + case VK_F2: + CreateConsole(-1); + return 0; + + case VK_F3: + CreateMulticastConsole(); + return 0; + + case VK_F4: + CreateMonitor(); + return 0; + + case VK_TAB: + return TRUE; + + break; + + + + } + return 0; + + case WM_TIMER: + + if (wParam == 1) // Slow = 10 secs + { + __try + { + time_t NOW = time(NULL); + struct tm * tm; + RefreshMainWindow(); + CheckTimer(); + TCPTimer(); + BBSSlowTimer(); + FWDTimerProc(); + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + CreateBBSTrafficReport(); + LastTrafficTime = NOW; + } + } + } + My__except_Routine("Slow Timer"); + } + else + __try + { + TrytoSend(); + TCPFastTimer(); + } + My__except_Routine("TrytoSend"); + + return (0); + + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)hActionMenu) + { + if (IsClipboardFormatAvailable(CF_TEXT)) + EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_ENABLED); + else + EnableMenuItem(hActionMenu,ID_ACTIONS_SENDMSGFROMCLIPBOARD, MF_BYCOMMAND | MF_GRAYED ); + + return TRUE; + } + + if (wParam == (WPARAM)hFWDMenu) + { + // Set up Forward Menu + + struct UserInfo * user; + char MenuLine[30]; + + for (user = BBSChain; user; user = user->BBSNext) + { + sprintf(MenuLine, "%s %d Msgs", user->Call, CountMessagestoForward(user)); + + if (ModifyMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, + MF_BYCOMMAND | MF_STRING, IDM_FORWARD_ALL + user->BBSNumber, MenuLine) == 0) + + AppendMenu(hFWDMenu, MF_STRING,IDM_FORWARD_ALL + user->BBSNumber, MenuLine); + } + return TRUE; + } + + if (wParam == (WPARAM)hDisMenu) + { + // Set up Disconnect Menu + + CIRCUIT * conn; + char MenuLine[30]; + int n; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + RemoveMenu(hDisMenu, IDM_DISCONNECT + n, MF_BYCOMMAND); + + if (conn->Active) + { + sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign); + AppendMenu(hDisMenu, MF_STRING, IDM_DISCONNECT + n, MenuLine); + } + } + return TRUE; + } + break; + + + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + + if (wmEvent == LBN_DBLCLK) + + break; + + if (wmId >= IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1) + { + // disconnect user + + conn=&Connections[wmId-IDM_DISCONNECT]; + + if (conn->Active) + { + Disconnect(conn->BPQStream); + } + } + + if (wmId >= IDM_FORWARD_ALL && wmId < IDM_FORWARD_ALL + 100) + { + StartForwarding(wmId - IDM_FORWARD_ALL, NULL); + return 0; + } + + switch (wmId) + { + case IDM_LOGBBS: + + ToggleParam(hMenu, hWnd, &LogBBS, IDM_LOGBBS); + break; + + case IDM_LOGCHAT: + + ToggleParam(hMenu, hWnd, &LogCHAT, IDM_LOGCHAT); + break; + + case IDM_LOGTCP: + + ToggleParam(hMenu, hWnd, &LogTCP, IDM_LOGTCP); + break; + + case IDM_HOUSEKEEPING: + + DoHouseKeeping(TRUE); + + break; + + case IDM_CONSOLE: + + CreateConsole(-1); + break; + + case IDM_MCMONITOR: + + CreateMulticastConsole(); + break; + + case IDM_MONITOR: + + CreateMonitor(); + break; + + case RESCANMSGS: + + ReRouteMessages(); + break; + + case IDM_IMPORT: + + ImportMessages(NULL, "", FALSE); + break; + + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + + case ID_HELP_ONLINEHELP: + + ShellExecute(hWnd,"open", + "http://www.cantab.net/users/john.wiseman/Documents/MailServer.html", + "", NULL, SW_SHOWNORMAL); + + break; + + case IDM_CONFIG: + DialogBox(hInst, MAKEINTRESOURCE(IDD_CONFIG), hWnd, ConfigWndProc); + break; + + case IDM_USERS: + DialogBox(hInst, MAKEINTRESOURCE(IDD_USEREDIT), hWnd, UserEditDialogProc); + break; + + case IDM_FWD: + DialogBox(hInst, MAKEINTRESOURCE(IDD_FORWARDING), hWnd, FwdEditDialogProc); + break; + + case IDM_MESSAGES: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGEDIT), hWnd, MsgEditDialogProc); + break; + + case IDM_WP: + DialogBox(hInst, MAKEINTRESOURCE(IDD_EDITWP), hWnd, WPEditDialogProc); + break; + + case ID_ACTIONS_SENDMSGFROMCLIPBOARD: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, ClpMsgDialogProc); + break; + + case ID_ACTIONS_SENDMESSAGE: + DialogBox(hInst, MAKEINTRESOURCE(IDD_MSGFROMCLIPBOARD), hWnd, SendMsgDialogProc); + break; + + case ID_MULTICAST: + + MulticastRX = !MulticastRX; + CheckMenuItem(hActionMenu, ID_MULTICAST, (MulticastRX) ? MF_CHECKED : MF_UNCHECKED); + break; + + case IDM_EXIT: + DestroyWindow(hWnd); + break; + + + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + case WM_SIZE: + + if (wParam == SIZE_MINIMIZED) + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + return (0); + + + case WM_SIZING: + { + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndSess, 0, 30, SessWidth, Height - 100, TRUE); + + return TRUE; + } + + + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + // TODO: Add any drawing code here... + EndPaint(hWnd, &ps); + break; + + case WM_DESTROY: + + GetWindowRect(MainWnd, &MainRect); // For save soutine + if (ConsHeader[0]->hConsole) + GetWindowRect(ConsHeader[0]->hConsole, &ConsHeader[0]->ConsoleRect); // For save soutine + if (ConsHeader[1]->hConsole) + GetWindowRect(ConsHeader[1]->hConsole, &ConsHeader[1]->ConsoleRect); // For save soutine + if (hMonitor) + GetWindowRect(hMonitor, &MonitorRect); // For save soutine + + KillTimer(hWnd,1); + KillTimer(hWnd,2); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +INT_PTR CALLBACK SendMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "B"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "P"); + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "T"); + + SendDlgItemMessage(hDlg, IDC_MSGTYPE, CB_SETCURSEL, 0, 0); + + return TRUE; + + case WM_SIZING: + { + HWND hWndEdit = GetDlgItem(hDlg, IDC_EDIT1); + + LPRECT lprc = (LPRECT) lParam; + int Height = lprc->bottom-lprc->top; + int Width = lprc->right-lprc->left; + + MoveWindow(hWndEdit, 5, 90, Width-20, Height - 140, TRUE); + + return TRUE; + } + + case WM_COMMAND: + + if (LOWORD(wParam) == IDSEND) + { + char status [3]; + struct MsgInfo * Msg; + char * via = NULL; + char BID[13]; + char FileList[32768]; + BIDRec * BIDRec; + int MsgLen; + char * MailBuffer; + char MsgFile[MAX_PATH]; + HANDLE hFile = INVALID_HANDLE_VALUE; + int WriteLen=0; + char HDest[61]; + char Destcopy[61]; + char * Vptr; + char * FileName[100]; + int FileLen[100]; + char * FileBody[100]; + int n, Files = 0; + int TotalFileSize = 0; + char * NewMsg; + + GetDlgItemText(hDlg, IDC_MSGTO, HDest, 60); + strcpy(Destcopy, HDest); + + GetDlgItemText(hDlg, IDC_MSGBID, BID, 13); + strlop(BID, ' '); + + GetDlgItemText(hDlg, IDC_ATTACHMENTS, FileList, 32767); + + // if there are attachments, check that they can be opened ane read + + n = 0; + + if (FileList[0]) + { + FILE * Handle; + struct stat STAT; + char * ptr1 = FileList, * ptr2; + + while(ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, ';'); + + if (ptr2) + *(ptr2++) = 0; + + FileName[n++] = ptr1; + + ptr1 = ptr2; + } + + FileName[n] = 0; + + // read the files + + Files = n; + n = 0; + + while (FileName[n]) + { + if (stat(FileName[n], &STAT) == -1) + { + char ErrorMessage[512]; + sprintf(ErrorMessage,"Can't find file %s", FileName[n]); + MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); + return TRUE; + } + + FileLen[n] = STAT.st_size; + + Handle = fopen(FileName[n], "rb"); + + if (Handle == NULL) + { + char ErrorMessage[512]; + sprintf(ErrorMessage,"Can't open file %s", FileName[n]); + MessageBox(NULL, ErrorMessage, "BPQMail", MB_ICONERROR); + return TRUE; + } + + FileBody[n] = malloc(FileLen[n]+1); + + fread(FileBody[n], 1, FileLen[n], Handle); + + fclose(Handle); + + TotalFileSize += FileLen[n]; + n++; + } + } + + if (strlen(HDest) == 0) + { + MessageBox(NULL, "To: Call Missing!", "BPQMail", MB_ICONERROR); + return TRUE; + } + + if (strlen(BID)) + { + if (LookupBID(BID)) + { + // Duplicate bid + + MessageBox(NULL, "Duplicate BID", "BPQMail", MB_ICONERROR); + return TRUE; + } + } + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, SYSOPCall); + + Vptr = strlop(Destcopy, '@'); + + if (Vptr == 0 && strchr(Destcopy, '!')) // Bang route without @ + { + Vptr = strchr(Destcopy, '!'); + strcpy(Msg->via, Vptr); + strlop(Destcopy, '!'); + + if (strlen(Destcopy) > 6) + memcpy(Msg->to, Destcopy, 6); + else + strcpy(Msg->to, Destcopy); + goto gotAddr; + } + + if (strlen(Destcopy) > 6) + memcpy(Msg->to, Destcopy, 6); + else + strcpy(Msg->to, Destcopy); + + _strupr(Msg->to); + + if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) + { + Vptr = HDest; + memmove(HDest, &HDest[4], strlen(HDest)); + strcpy(Msg->to, "RMS"); + + } + else if (_memicmp(HDest, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + Vptr = HDest; + memmove(HDest, &HDest[5], strlen(HDest)); + Msg->to[0] = 0; + } + } + else if (Vptr) + { + // If looks like a valid email address, treat as such + + int tolen = (Vptr - Destcopy) - 1; + + if (tolen > 6 || !CheckifPacket(Vptr)) + { + // Assume Email address + + Vptr = HDest; + + if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route + strcpy(Msg->to, "RMS"); + else if (ISP_Gateway_Enabled) + Msg->to[0] = 0; + else + { + MessageBox(NULL, "Sending to Internet Email not available", "BPQMail", MB_ICONERROR); + return TRUE; + } + } + } + if (Vptr) + { + if (strlen(Vptr) > 40) + Vptr[40] = 0; + + strcpy(Msg->via, Vptr); + } +gotAddr: + GetDlgItemText(hDlg, IDC_MSGTITLE, Msg->title, 61); + GetDlgItemText(hDlg, IDC_MSGTYPE, status, 2); + Msg->type = status[0]; + Msg->status = 'N'; + + if (strlen(BID) == 0) + sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); + + strcpy(Msg->bid, BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + MsgLen = SendDlgItemMessage(hDlg, IDC_EDIT1, WM_GETTEXTLENGTH, 0 ,0); + + MailBuffer = malloc(MsgLen + TotalFileSize + 2000); // Allow for a B2 Header if attachments + + if (Files) + { + char DateString[80]; + struct tm * tm; + + char Type[16] = "Private"; + + // Get Type + + if (Msg->type == 'B') + strcpy(Type, "Bulletin"); + else if (Msg->type == 'T') + strcpy(Type, "Traffic"); + + // Create a B2 Message + + // B2 Header + + NewMsg = MailBuffer + 1000; + + tm = gmtime((time_t *)&Msg->datecreated); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + // Remove last Source Route + + if (strchr(HDest, '!')) + { + char * bang = HDest + strlen(HDest); + + while (*(--bang) != '!'); // Find last ! + + *(bang) = 0; // remove it; + } + + NewMsg += sprintf(NewMsg, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n", + Msg->bid, DateString, Type, Msg->from, HDest, Msg->title, BBSName); + + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", MsgLen); + + for (n = 0; n < Files; n++) + { + char * p = FileName[n], * q; + + // Remove any path + + q = strchr(p, '\\'); + + while (q) + { + if (q) + *q++ = 0; + p = q; + q = strchr(p, '\\'); + } + + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + GetDlgItemText(hDlg, IDC_EDIT1, NewMsg, MsgLen+1); + NewMsg += MsgLen; + NewMsg += sprintf(NewMsg, "\r\n"); + + for (n = 0; n < Files; n++) + { + memcpy(NewMsg, FileBody[n], FileLen[n]); + NewMsg += FileLen[n]; + free(FileBody[n]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + Msg->length = NewMsg - (MailBuffer + 1000); + NewMsg = MailBuffer + 1000; + Msg->B2Flags = B2Msg | Attachments; + } + + else + { + GetDlgItemText(hDlg, IDC_EDIT1, MailBuffer, MsgLen+1); + Msg->length = MsgLen; + NewMsg = MailBuffer; + } + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = CreateFile(MsgFile, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile != INVALID_HANDLE_VALUE) + { + WriteFile(hFile, NewMsg, Msg->length, &WriteLen, NULL); + CloseHandle(hFile); + } + + free(MailBuffer); + + MatchMessagetoBBSList(Msg, 0); + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + EndDialog(hDlg, LOWORD(wParam)); + + return TRUE; + } + + + if (LOWORD(wParam) == IDSelectFiles) + { + char FileNames[2048]; + char FullFileNames[32768]; + OPENFILENAME Ofn; + int err; + + FileNames[0] = 0; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = hDlg; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileNames; + Ofn.nMaxFile = 2048; + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = (LPSTR)NULL; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; + Ofn.lpstrTitle = NULL;//; + + if (GetOpenFileName(&Ofn)) + { + // if one is selected, a single string is returned, if more than one, a single + // path, followed by all the strings, duuble null terminated. + + char * Names[101]; // Allow up to 100 names + int n = 0; + char * ptr = FileNames; + + while (*ptr) + { + Names[n++] = ptr; + ptr += strlen(ptr); + ptr++; + } + + GetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames, 32768); + + if (strlen(FullFileNames)) + strcat(FullFileNames, ";"); + + if (n == 1) + { + // Single Select + + strcat(FullFileNames, FileNames); + } + else + { + int i = 1; + + while(i < n) + { + strcat(FullFileNames, Names[0]); + strcat(FullFileNames, "\\"); + strcat(FullFileNames, Names[i]); + i++; + if (i < n) + strcat(FullFileNames, ";"); + } + } + SetDlgItemText(hDlg, IDC_ATTACHMENTS, FullFileNames); + } + else + err = GetLastError(); + return (INT_PTR)TRUE; + } + + + if (LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +INT_PTR CALLBACK ClpMsgDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HGLOBAL hglb; + LPTSTR lptstr; + + switch (message) + { + case WM_INITDIALOG: + + SetWindowText(hDlg, "Send Message from Clipboard"); + + if (!IsClipboardFormatAvailable(CF_TEXT)) + break; + + if (!OpenClipboard(hDlg)) + break; + + hglb = GetClipboardData(CF_TEXT); + + if (hglb != NULL) + { + lptstr = GlobalLock(hglb); + + if (lptstr != NULL) + { + SetDlgItemText(hDlg, IDC_EDIT1, lptstr); + GlobalUnlock(hglb); + } + } + CloseClipboard(); + } + + return SendMsgDialogProc(hDlg, message, wParam, lParam); + +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +SMTPMsgs = 0; + +int RefreshMainWindow() +{ + char msg[80]; + CIRCUIT * conn; + int i,n, SYSOPMsgs = 0, HeldMsgs = 0; + time_t now; + struct tm * tm; + char tim[20]; + + SendDlgItemMessage(MainWnd,100,LB_RESETCONTENT,0,0); + + SMTPMsgs = 0; + + for (n = 0; n < NumberofStreams; n++) + { + conn=&Connections[n]; + + if (!conn->Active) + { + strcpy(msg,"Idle"); + } + else + { + { + if (conn->UserPointer == 0) + strcpy(msg,"Logging in"); + else + { + i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d", + conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream, + "BBS", conn->OutputQueueLength - conn->OutputGetPointer); + } + } + } + SendDlgItemMessage(MainWnd,100,LB_ADDSTRING,0,(LPARAM)msg); + } + + SetDlgItemInt(hWnd, IDC_MSGS, NumberofMessages, FALSE); + + n = 0; + + for (i=1; i <= NumberofMessages; i++) + { + if (MsgHddrPtr[i]->status == 'N') + { + if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0) + SYSOPMsgs++; + else + if (MsgHddrPtr[i]->to[0] == 0) + SMTPMsgs++; + } + else + { + if (MsgHddrPtr[i]->status == 'H') + HeldMsgs++; + } + } + + SetDlgItemInt(hWnd, IDC_SYSOPMSGS, SYSOPMsgs, FALSE); + SetDlgItemInt(hWnd, IDC_HELD, HeldMsgs, FALSE); + SetDlgItemInt(hWnd, IDC_SMTP, SMTPMsgs, FALSE); + + SetDlgItemInt(hWnd, IDC_MSGSEM, MsgNoSemaphore.Clashes, FALSE); + SetDlgItemInt(hWnd, IDC_ALLOCSEM, AllocSemaphore.Clashes, FALSE); + SetDlgItemInt(hWnd, IDC_CONSEM, ConSemaphore.Clashes, FALSE); + + now = time(NULL); + + tm = gmtime(&now); + sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); + SetDlgItemText(hWnd, IDC_UTC, tim); + + tm = localtime(&now); + sprintf_s(tim, sizeof(tim), "%02d:%02d", tm->tm_hour, tm->tm_min); + SetDlgItemText(hWnd, IDC_LOCAL, tim); + + + return 0; +} + +#define MAX_PENDING_CONNECTS 4 + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +SOCKADDR_IN local_sin; /* Local socket - internet style */ + +PSOCKADDR_IN psin; + +SOCKET sock; + + + +BOOL Initialise() +{ + int i, len; + ConnectionInfo * conn; + struct UserInfo * user = NULL; + HKEY hKey=0; + char * ptr1; + int Attrs, ret; + char msg[500]; + TIME_ZONE_INFORMATION TimeZoneInformation; + struct stat STAT; + + GetTimeZoneInformation(&TimeZoneInformation); + + _tzset(); + _MYTIMEZONE = timezone; + _MYTIMEZONE = TimeZoneInformation.Bias * 60; + + // Register message for posting by BPQDLL + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + // See if we need to warn of possible problem with BaseDir moved by installer + + strcpy(BPQDirectory, GetBPQDirectory()); + + sprintf(BaseDir, "%s/BPQMailChat", BPQDirectory); + + len = strlen(BaseDir); + ptr1 = BaseDir; + + while (*ptr1) + { + if (*(ptr1) == '/') *(ptr1) = '\\'; + ptr1++; + } + + // Make Sure BASEDIR Exists + + Attrs = GetFileAttributes(BaseDir); + + if (Attrs == -1) + { + sprintf_s(msg, sizeof(msg), "Base Directory %s not found - should it be created?", BaseDir); + ret = MessageBox(NULL, msg, "BPQMail", MB_YESNO); + + if (ret == IDYES) + { + ret = CreateDirectory(BaseDir, NULL); + if (ret == 0) + { + MessageBox(NULL, "Failed to created Base Directory - exiting", "BPQMail", MB_ICONSTOP); + return FALSE; + } + } + else + { + MessageBox(NULL, "Can't Continue without a Base Directory - exiting", "BPQMailChat", MB_ICONSTOP); + return FALSE; + } + } + else + { + if (!(Attrs & FILE_ATTRIBUTE_DIRECTORY)) + { + sprintf_s(msg, sizeof(msg), "Base Directory %s is a file not a directory - exiting", BaseDir); + ret = MessageBox(NULL, msg, "BPQMail", MB_ICONSTOP); + + return FALSE; + } + } + + initUTF8(); + + // Set up file and directory names + + strcpy(UserDatabasePath, BaseDir); + strcat(UserDatabasePath, "\\"); + strcat(UserDatabasePath, UserDatabaseName); + + strcpy(MsgDatabasePath, BaseDir); + strcat(MsgDatabasePath, "\\"); + strcat(MsgDatabasePath, MsgDatabaseName); + + strcpy(BIDDatabasePath, BaseDir); + strcat(BIDDatabasePath, "\\"); + strcat(BIDDatabasePath, BIDDatabaseName); + + strcpy(WPDatabasePath, BaseDir); + strcat(WPDatabasePath, "\\"); + strcat(WPDatabasePath, WPDatabaseName); + + strcpy(BadWordsPath, BaseDir); + strcat(BadWordsPath, "\\"); + strcat(BadWordsPath, BadWordsName); + + strcpy(NTSAliasesPath, BaseDir); + strcat(NTSAliasesPath, "/"); + strcat(NTSAliasesPath, NTSAliasesName); + + strcpy(MailDir, BaseDir); + strcat(MailDir, "\\"); + strcat(MailDir, "Mail"); + + CreateDirectory(MailDir, NULL); // Just in case + + strcpy(ConfigName, BaseDir); + strcat(ConfigName, "\\"); + strcat(ConfigName, "BPQMail.cfg"); + + UsingingRegConfig = FALSE; + + // if config file exists use it else try to get from Registry + + if (stat(ConfigName, &STAT) == -1) + { + UsingingRegConfig = TRUE; + + if (GetConfigFromRegistry()) + { + SaveConfig(ConfigName); + } + else + { + int retCode; + + strcpy(BBSName, GetNodeCall()); + strlop(BBSName, '-'); + strlop(BBSName, ' '); + + sprintf(msg, "No configuration found - Dummy Config created"); + + retCode = MessageBox(NULL, msg, "BPQMailChat", MB_OKCANCEL); + + if (retCode == IDCANCEL) + return FALSE; + + SaveConfig(ConfigName); + } + } + + if (GetConfig(ConfigName) == EXIT_FAILURE) + { + ret = MessageBox(NULL, + "BBS Config File seems corrupt - check before continuing", "BPQMail", MB_ICONSTOP); + return FALSE; + } + + // Got a Config File + + if (MainRect.right < 100 || MainRect.bottom < 100) + { + GetWindowRect(MainWnd, &MainRect); + } + + MoveWindow(MainWnd, MainRect.left, MainRect.top, MainRect.right-MainRect.left, MainRect.bottom-MainRect.top, TRUE); + + if (OpenMon) + CreateMonitor(); + + BBSApplMask = 1<<(BBSApplNum-1); + + ShowWindow(GetDlgItem(MainWnd, 901), SW_HIDE); + ShowWindow(GetDlgItem(MainWnd, 902), SW_HIDE); + ShowWindow(GetDlgItem(MainWnd, 903), SW_HIDE); + + // Make backup copies of Databases + + CopyBIDDatabase(); + CopyMessageDatabase(); + CopyUserDatabase(); + CopyWPDatabase(); + + SetupMyHA(); + SetupFwdAliases(); + SetupNTSAliases(NTSAliasesPath); + + GetWPDatabase(); + GetMessageDatabase(); + GetUserDatabase(); + GetBIDDatabase(); + GetBadWordFile(); + GetHTMLForms(); + + UsingingRegConfig = FALSE; + + // Make sure SYSOPCALL is set + + if (SYSOPCall[0] == 0) + strcpy(SYSOPCall, BBSName); + + // Make sure there is a user record for the BBS, with BBS bit set. + + user = LookupCall(BBSName); + + if (user == NULL) + { + user = AllocateUserRecord(BBSName); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + } + + // if forwarding AMPR mail make sure User/BBS AMPR exists + + if (SendAMPRDirect) + { + BOOL NeedSave = FALSE; + + user = LookupCall("AMPR"); + + if (user == NULL) + { + user = AllocateUserRecord("AMPR"); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + NeedSave = TRUE; + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + NeedSave = TRUE; + } + + if (NeedSave) + SaveUserDatabase(); + } + + // Allocate Streams + + for (i=0; i < MaxStreams; i++) + { + conn = &Connections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofStreams++; + + BPQSetHandle(conn->BPQStream, hWnd); + + SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask | ChatApplMask); + Disconnect(conn->BPQStream); + } + + InitialiseTCP(); + + InitialiseNNTP(); + + SetupListenSet(); // Master set of listening sockets + + if (BBSApplNum) + { + SetupUIInterface(); + if (MailForInterval) + _beginthread(SendMailForThread, 0, 0); + } + + if (cfgMinToTray) + { + AddTrayMenuItem(MainWnd, "Mail Server"); + } + + SetTimer(hWnd,1,10000,NULL); // Slow Timer (10 Secs) + SetTimer(hWnd,2,100,NULL); // Send to Node and TCP Poll (100 ms) + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = _mkgmtime(tm); + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); + + if (LastHouseKeepingTime) + { + if ((now - LastHouseKeepingTime) > MaintInterval * 3600) + { + DoHouseKeeping(FALSE); + } + } + } + + if (strstr(CmdLine, "tidymail")) + DeleteRedundantMessages(); + + if (strstr(CmdLine, "nohomebbs")) + DontNeedHomeBBS = TRUE; + + if (strstr(CmdLine, "DontCheckFromCall")) + DontCheckFromCall = TRUE; + + CheckMenuItem(hMenu,IDM_LOGBBS, (LogBBS) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_LOGTCP, (LogTCP) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_LOGCHAT, (LogCHAT) ? MF_CHECKED : MF_UNCHECKED); + + RefreshMainWindow(); + +// CreateWPReport(); + + CreatePipeThread(); + + return TRUE; +} + +int ConnectState(Stream) +{ + int state; + + SessionStateNoAck(Stream, &state); + return state; +} +UCHAR * EncodeCall(UCHAR * Call) +{ + static char axcall[10]; + + ConvToAX25(Call, axcall); + return &axcall[0]; + +} + +/* +VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo) +{ + struct UserInfo * user; + + int i = FWDInfo->UserIndex; + + if (i == -1) + { + FWDInfo->UserIndex = FWDInfo->UserCall[0] = 0; // Not scanning users + } + + for (i++; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + FWDInfo->UserIndex = i; + strcpy(FWDInfo->UserCall, user->Call); + FWDInfo->FwdTimer = FWDInfo->FwdInterval - 20; + return ; + } + } + + // Finished Scan + + FWDInfo->UserIndex = FWDInfo->FwdTimer = FWDInfo->UserCall[0] = 0; +} +*/ + +#ifndef NEWROUTING + +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo) +{ +} +VOID SetupMyHA() +{ +} +VOID SetupFwdAliases() +{ +} + +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn) +{ + struct UserInfo * bbs; + struct BBSForwardingInfo * ForwardingInfo; + char ATBBS[41]; + char * HRoute; + int Count =0; + + strcpy(ATBBS, Msg->via); + HRoute = strlop(ATBBS, '.'); + + if (Msg->type == 'P') + { + // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSToList(Msg, bbs, ForwardingInfo)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckBBSHList(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + return 1; + } + } + + return FALSE; + } + + // Bulls go to all matching BBSs, so the order of checking doesn't matter + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + ForwardingInfo = bbs->ForwardingInfo; + + if (CheckABBS(Msg, bbs, ForwardingInfo, ATBBS, HRoute)) + { + if (_stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! + { + if ((conn == NULL) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0)) // Dont send back + { + set_fwd_bit(Msg->fbbs, bbs->BBSNumber); + ForwardingInfo->MsgCount++; + } + } + Count++; + } + } + + return Count; +} +BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) +{ + char ** Calls; + char ** HRoutes; + int i, j; + + if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS + return TRUE; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + + // Check AT distributions + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + if ((HRoute) && (ForwardingInfo->Haddresses)) + { + // Match on Routes + + HRoutes = ForwardingInfo->Haddresses; + + while(HRoutes[0]) + { + i = strlen(HRoutes[0]) - 1; + j = strlen(HRoute) - 1; + + while ((i >= 0) && (j >= 0)) // Until one string rus out + { + if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards + goto next; + } + + return TRUE; + next: + HRoutes++; + } + } + + + return FALSE; + +} + +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Calls; + + // Check TO distributions + + if (ForwardingInfo->TOCalls) + { + Calls = ForwardingInfo->TOCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], Msg->to) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) +{ + char ** Calls; + + // Check AT distributions + + if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS + return TRUE; + + if (ForwardingInfo->ATCalls) + { + Calls = ForwardingInfo->ATCalls; + + while(Calls[0]) + { + if (strcmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + return FALSE; +} + +BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute) +{ + char ** HRoutes; + int i, j; + + if ((HRoute) && (ForwardingInfo->Haddresses)) + { + // Match on Routes + + HRoutes = ForwardingInfo->Haddresses; + + while(HRoutes[0]) + { + i = strlen(HRoutes[0]) - 1; + j = strlen(HRoute) - 1; + + while ((i >= 0) && (j >= 0)) // Until one string rus out + { + if (HRoutes[0][i--] != HRoute[j--]) // Compare backwards + goto next; + } + + return TRUE; + next: + HRoutes++; + } + } + return FALSE; +} + +#endif + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr; + + if (buf == NULL) return NULL; // Protect + + ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} diff --git a/BPQMail.vcxproj b/BPQMail.vcxproj new file mode 100644 index 0000000..2765bdf --- /dev/null +++ b/BPQMail.vcxproj @@ -0,0 +1,346 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {3766AA10-C777-4ED8-A83D-F1452DE9B665} + TelnetServer + Win32Proj + + + + Application + v141 + NotSet + true + + + Application + v141 + NotSet + true + + + Application + v141 + false + NotSet + + + Application + v141 + false + NotSet + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.28307.799 + + + C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ + true + + + true + + + C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ + false + + + false + + + + + + + Disabled + ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + true + + Level3 + EditAndContinue + CompileAsC + + + ..\Include;%(AdditionalIncludeDirectories) + + + ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) + c:\DevProgs\bpq32\BPQMail.exe + false + LIBCMT;%(IgnoreSpecificDefaultLibraries) + true + $(IntDir)$(TargetName).pdb + true + $(IntDir)BBSListings\bpqmail.map + true + Windows + MachineX86 + + + + + + + + + Disabled + ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + true + + + Level3 + ProgramDatabase + CompileAsC + + + ..\Include;%(AdditionalIncludeDirectories) + + + ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) + c:\DevProgs\bpq32\BPQMail.exe + false + LIBCMT;%(IgnoreSpecificDefaultLibraries) + true + $(IntDir)$(TargetName).pdb + true + $(IntDir)BBSListings\bpqmail.map + true + Windows + + + + + + + + + + + MaxSpeed + false + ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + MultiThreaded + + Level3 + ProgramDatabase + CompileAsC + + + ..\Include;%(AdditionalIncludeDirectories) + + + ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) + c:\DevProgs\bpq32\BPQMail.exe + true + c:\DevProgs\bpq32\BPQMail.pdb + true + c:\DevProgs\bpq32\BPQMail.map + Windows + true + true + + MachineX86 + + + + + + + + + + + + + MaxSpeed + false + ..\CKernel;..\CInclude;..\CommonSource;..\BPQMail;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + CompileAsC + + + ..\Include;%(AdditionalIncludeDirectories) + + + ..\lib\bpq32.lib;wsock32.lib;comctl32.lib;winmm.lib;..\lib\libconfig.lib;DbgHelp.lib;%(AdditionalDependencies) + c:\DevProgs\bpq32\BPQMail.exe + true + c:\DevProgs\bpq32\BPQMail.pdb + true + c:\DevProgs\bpq32\BPQMail.map + Windows + true + true + + + + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + All + All + $(IntDir) + $(IntDir) + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + All + All + $(IntDir) + $(IntDir) + $(IntDir) + $(IntDir) + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.obj + $(IntDir)%(Filename)1.xdc + $(IntDir)%(Filename)1.xdc + + + + + + + + + + + + + \ No newline at end of file diff --git a/BPQMail.vcxproj.filters b/BPQMail.vcxproj.filters new file mode 100644 index 0000000..982128f --- /dev/null +++ b/BPQMail.vcxproj.filters @@ -0,0 +1,113 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/BPQMail.vcxproj.user b/BPQMail.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/BPQMail.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/BPQTermMDI.c b/BPQTermMDI.c index 9626dc2..54ef348 100644 --- a/BPQTermMDI.c +++ b/BPQTermMDI.c @@ -92,7 +92,6 @@ HBRUSH bgBrush; extern BOOL FrameMaximized; char RTFHeader[4000]; - int RTFHddrLen; struct ConsoleInfo * ConsHeader = NULL; @@ -241,11 +240,6 @@ int ClientHeight, ClientWidth; #define MAXLINES 1000 #define LINELEN 200 -char RTFHeader[4000]; - -int RTFHddrLen; - - char FontName[100] = "FixedSys"; int FontSize = 20; int FontWidth = 8; diff --git a/BPQtoAGW.c b/BPQtoAGW.c index 247ee03..3c07458 100644 --- a/BPQtoAGW.c +++ b/BPQtoAGW.c @@ -495,7 +495,7 @@ static int ProcessLine(char * buf, int Port, BOOL CheckPort) BPQport = Port; p_ipad = ptr; } - if(BPQport > 0 && BPQport <33) + if(BPQport > 0 && BPQport < MAXBPQPORTS) { p_udpport = strtok(NULL, " \t\n\r"); diff --git a/Bpq32.c b/Bpq32.c index 463913d..a4d19f6 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -89,8 +89,8 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // 410c December 2007 -// Fix problem with NT introduced in V410a -// Display location of DLL on Console +// Fix problem with NT introduced in V410a +// Display location of DLL on Console // 410d January 2008 @@ -1165,7 +1165,14 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Support 64 ports (69) // Fix Node commands for setting UZ7HO Modem (70) // Fix processing SABM on an existing session (71) - +// Extend KISS Node command to send more than one parameter byte (72) +// Add G7TAJ's code to record activity of HF ports for stats display (72) +// Add option to send KISS command to TNC on startup (73) +// Fix Bug in DED Emulator Monitor code (74) +// Add Filters to DED Monitor code (75) +// Detect loss of DED application (76) +// Fix connects to Application Alias with UZ7HO Driver (76) +// Fix Interlock of ports on same UZ7HO modem. (76) #define CKernel diff --git a/CBPQ32.vcproj b/CBPQ32.vcproj index 6a00653..f8e9834 100644 --- a/CBPQ32.vcproj +++ b/CBPQ32.vcproj @@ -50,7 +50,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\CInclude;..\CommonSource;..\CKernel" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;BPQ32" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="1" diff --git a/CHeaders.h b/CHeaders.h index 81d6758..0d6886e 100644 --- a/CHeaders.h +++ b/CHeaders.h @@ -291,7 +291,7 @@ extern int FULL_CTEXT; // CTEXT ON ALL CONNECTS IF NZ // Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 -extern BPQVECSTRUC DUMMY; // Needed to force correct order of following +extern BPQVECSTRUC DUMMYVEC; // Needed to force correct order of following extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; @@ -370,8 +370,8 @@ extern int MINBUFFCOUNT; extern UCHAR BPQDirectory[]; extern UCHAR BPQProgramDirectory[]; -extern char WINMOR[]; -extern char PACTORCALL[]; +extern UCHAR WINMOR[]; +extern UCHAR PACTORCALL[]; extern UCHAR MCOM; extern UCHAR MUIONLY; diff --git a/ChatUtils.c b/ChatUtils.c index 651ac6c..ce7cc07 100644 --- a/ChatUtils.c +++ b/ChatUtils.c @@ -528,8 +528,8 @@ VOID FreeList(char ** Hddr) #include "libconfig.h" -config_t cfg; -config_setting_t * group; +static config_t cfg; +static config_setting_t * group; extern char ChatWelcomeMsg[1000]; diff --git a/Cmd.c b/Cmd.c index b5d0bf6..617d6e9 100644 --- a/Cmd.c +++ b/Cmd.c @@ -3963,7 +3963,7 @@ VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX if (rxInterlock || txInterlock) { - for (i=1; i<33; i++) + for (i=1; i <= MAXBPQPORTS; i++) { OtherTNC = TNCInfo[i]; @@ -5059,7 +5059,6 @@ VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX } VOID CloseKISSPort(struct PORTCONTROL * PortVector); -int OpenConnection(struct PORTCONTROL * PortVector); VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) { @@ -5330,7 +5329,7 @@ VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX return; } - if (OpenConnection(PORT), TRUE) + if (OpenConnection(PORT)) Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r"); else Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r"); @@ -5355,9 +5354,8 @@ VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX -#define FEND 0xC0 int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count); - +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD) { @@ -5365,9 +5363,12 @@ VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * char * ptr, * Context; int portno = 0; - int cmd = 0, val = 0; struct PORTCONTROL * PORT = PORTTABLE; int n = NUMBEROFPORTS; + UCHAR KissString[128]; + UCHAR ENCBUFF[256]; + int KissLen = 0; + unsigned char * Kissptr = KissString; // Send KISS Command to TNC @@ -5380,17 +5381,16 @@ VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * portno = atoi (ptr); ptr = strtok_s(NULL, " ", &Context); - if (ptr) + while (ptr && ptr[0] && KissLen < 120) { - cmd = atoi (ptr); + *(Kissptr++) = atoi (ptr); + KissLen++; ptr = strtok_s(NULL, " ", &Context); - if (ptr) - val = atoi (ptr); } } - if (portno == 0 || cmd == 0) + if (portno == 0 || KissLen == 0) { strcpy(Bufferptr, BADMSG); Bufferptr += (int)strlen(BADMSG); @@ -5403,8 +5403,6 @@ VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * if (PORT->PORTNUMBER == portno) { struct KISSINFO * KISS; - UCHAR ENCBUFF[16]; - unsigned char * ptr = ENCBUFF; if (PORT->PORTTYPE != 0 && PORT->PORTTYPE != 22) { @@ -5424,17 +5422,14 @@ VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * // Send Command - *(ptr++) = FEND; - *(ptr++) = KISS->OURCTRL | cmd; - *(ptr++) = (UCHAR)val; - *(ptr++) = FEND; + KissLen = KissEncode(KissString, ENCBUFF, KissLen); PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q PORT->Session = Session; PORT->LastKISSCmdTime = time(NULL); - ASYSEND(PORT, ENCBUFF, 4); + ASYSEND(PORT, ENCBUFF, KissLen); Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r"); SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); diff --git a/CommonCode.c b/CommonCode.c index 1a1dfd6..7060c77 100644 --- a/CommonCode.c +++ b/CommonCode.c @@ -913,9 +913,8 @@ BOOL ProcessIncommingConnectEx(struct TNCINFO * TNC, char * Call, int Stream, BO PMSGWITHLEN buffptr; int Totallen = 0; UCHAR * ptr; - struct PORTCONTROL * PORT; + struct PORTCONTROL * PORT = TNC->PortRecord; - PORT = &TNC->PortRecord->PORTCONTROL; // Stop Scanner @@ -3264,7 +3263,7 @@ VOID SendReportMsg(char * buff, int txlen) buff[txlen++] = (crc&0xff); buff[txlen++] = (crc>>8); - sendto(ReportSocket, buff, txlen, 0, (LPSOCKADDR)&reportdest, sizeof(reportdest)); + sendto(ReportSocket, buff, txlen, 0, (struct sockaddr *)&reportdest, sizeof(reportdest)); } VOID SendLocation() @@ -4160,7 +4159,7 @@ VOID GetUIConfig() } #else - + int retCode, Vallen, Type, i; char Key[80]; char Size[80]; diff --git a/FLDigi.c b/FLDigi.c index 555c41c..b46ee65 100644 --- a/FLDigi.c +++ b/FLDigi.c @@ -25,7 +25,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include "CHeaders.h" -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); @@ -218,6 +218,14 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) // 100 mS Timer. + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + // See if waiting for busy to clear before sending a connect if (TNC->BusyDelay) @@ -1122,7 +1130,7 @@ static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) return Len; } -VOID FLDIGISuspendPort(struct TNCINFO * TNC) +VOID FLDIGISuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { TNC->FLInfo->CONOK = FALSE; } diff --git a/FreeDATA.c b/FreeDATA.c index 75c7128..7b92f21 100644 --- a/FreeDATA.c +++ b/FreeDATA.c @@ -43,7 +43,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses int KillTNC(struct TNCINFO * TNC); static int RestartTNC(struct TNCINFO * TNC); -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); static int Socket_Data(int sock, int error, int eventcode); VOID MoveWindows(struct TNCINFO * TNC); @@ -73,7 +73,7 @@ int FreeDataDisconnect(struct TNCINFO * TNC); int FreeGetData(struct TNCINFO * TNC); static void SendBeacon(struct TNCINFO * TNC, int Interval); void buildParamString(struct TNCINFO * TNC, char * line); -VOID FreeDataSuspendPort(struct TNCINFO * TNC); +VOID FreeDataSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC); VOID FreeDataReleasePort(struct TNCINFO * TNC); @@ -450,9 +450,17 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { case 7: - // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances - SendPoll(TNC); + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + SendPoll(TNC); // Check for buffered data to send @@ -599,7 +607,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) // Stop Listening, and set MYCALL to user's call - FreeDataSuspendPort(TNC); + FreeDataSuspendPort(TNC, TNC); FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit @@ -1049,7 +1057,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) if (TNC->ConnectPending == 0 && TNC->PTTState == 0) { - FreeDataSuspendPort(TNC); + FreeDataSuspendPort(TNC, TNC); TNC->GavePermission = TRUE; return 0; // OK to Change } @@ -1098,7 +1106,7 @@ VOID FreeDataReleaseTNC(struct TNCINFO * TNC) } -VOID FreeDataSuspendPort(struct TNCINFO * TNC) +VOID FreeDataSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { // char CMD[] = "{\"type\" : \"set\", \"command\" : \"listen\", \"state\": \"False\"}\n"; // send(TNC->TCPDataSock, CMD, strlen(CMD), 0); diff --git a/GetVersion.h b/GetVersion.h index 30c0161..c239ae1 100644 --- a/GetVersion.h +++ b/GetVersion.h @@ -4,7 +4,7 @@ char VersionStringWithBuild[50]=""; int Ver[4] = {Vers}; char TextVerstring[50] = ""; -VOID GetVersionInfo(TCHAR * File) +void GetVersionInfo(char * File) { #ifndef LINBPQ diff --git a/HFCommon.c b/HFCommon.c index 295a418..be42afa 100644 --- a/HFCommon.c +++ b/HFCommon.c @@ -1623,7 +1623,7 @@ BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC) if (rxInterlock == 0 && txInterlock == 0) return ThisTNC->Busy; // No Interlock - for (i=1; i<33; i++) + for (i=1; i <= MAXBPQPORTS; i++) { TNC = TNCInfo[i]; diff --git a/HSMODEM.c b/HSMODEM.c index 0529e4e..5362b87 100644 --- a/HSMODEM.c +++ b/HSMODEM.c @@ -115,7 +115,7 @@ struct HSMODEMINFO int KillTNC(struct TNCINFO * TNC); int RestartTNC(struct TNCINFO * TNC); -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); #include "bpq32.h" @@ -283,7 +283,7 @@ static int ProcessLine(char * buf, int Port) return (TRUE); } -char * Config; +static char * Config; static char * ptr1, * ptr2; int HSMODEMGetLine(char * buf) @@ -413,7 +413,15 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances - if (TNC->CONNECTED) + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + if (TNC->CONNECTED) { TNC->CONNECTED--; @@ -890,7 +898,7 @@ VOID HSMODEMReleaseTNC(struct TNCINFO * TNC) } -VOID HSMODEMSuspendPort(struct TNCINFO * TNC) +VOID HSMODEMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { HSMODEMSendCommand(TNC, "CONOK OFF\r"); } diff --git a/HTTPcode.c b/HTTPcode.c index 6a50eb9..7260c8e 100644 --- a/HTTPcode.c +++ b/HTTPcode.c @@ -1382,7 +1382,7 @@ int SetupNodeMenu(char * Buff, int LOCAL) Len = sprintf(Buff, NodeMenuHeader, Mycall); - for (i=1; i<33; i++) + for (i=1; i <= MAXBPQPORTS; i++) { TNC = TNCInfo[i]; if (TNC == NULL) diff --git a/HanksRT.c b/HanksRT.c index e87ce89..98ce668 100644 --- a/HanksRT.c +++ b/HanksRT.c @@ -135,10 +135,10 @@ int AddtoHistory(struct user_t * user, char * text) struct tm * tm; time_t Now = time(NULL); - // Don't want to grow indefinitely and fill memory. We only allow display upt 24 hours back, so if first record is older that that + // Don't want to grow indefinitely and fill memory. We only allow display up to 24 hours back, so if first record is older that that // remove and reuse it - if (History && History->Time < Now - 86400) + if (History && History->Time < Now - 86400) { Rec = History; History = Rec->next; // Remove from front of chain @@ -183,9 +183,6 @@ int AddtoHistory(struct user_t * user, char * text) - - - int ChatIsUTF8(unsigned char *ptr, int len) { int n; @@ -785,9 +782,7 @@ VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffe int n = HistoryCount; if (param) - interval = atoi(param) * 60; - - start = time(NULL) - interval; + interval = atoi(param); if (interval < 1) { @@ -802,6 +797,8 @@ VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffe interval = 1440; // Limit to 1 day } + start = time(NULL) - (interval * 60); + // Find first record to send while (ptr) diff --git a/KAMPactor.c b/KAMPactor.c index 08e475c..f214f04 100644 --- a/KAMPactor.c +++ b/KAMPactor.c @@ -331,6 +331,14 @@ ok: // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + CheckRXKAM(TNC); KAMPoll(port); diff --git a/KISSHF.c b/KISSHF.c index 884fb28..a8aeb41 100644 --- a/KISSHF.c +++ b/KISSHF.c @@ -30,7 +30,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include "CHeaders.h" -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); #include "bpq32.h" @@ -720,7 +720,7 @@ VOID KISSHFReleaseTNC(struct TNCINFO * TNC) ReleaseOtherPorts(TNC); } -VOID KISSHFSuspendPort(struct TNCINFO * TNC) +VOID KISSHFSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { } diff --git a/L2Code.c b/L2Code.c index 8651fdb..1b4d8f7 100644 --- a/L2Code.c +++ b/L2Code.c @@ -37,7 +37,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #define REJSENT 1 // SET WHEN FIRST REJ IS SENT IN REPLY // TO AN I(P) -#define RNRSET 2 // RNR RECEIVED FROM OTHER END +#define RNRSET 0x2 // RNR RECEIVED FROM OTHER END #define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED #define RNRSENT 0x10 // WE HAVE SEND RNR #define POLLSENT 0x20 // POLL BIT OUTSTANDING @@ -127,7 +127,7 @@ UCHAR ALIASMSG = 0; extern UINT APPLMASK; static UCHAR ISNETROMMSG = 0; UCHAR MSGFLAG = 0; -extern UCHAR * ALIASPTR; +extern char * ALIASPTR; UCHAR QSTCALL[7] = {'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; // QST IN AX25 UCHAR NODECALL[7] = {0x9C, 0x9E, 0x88, 0x8A, 0xA6, 0x40, 0xE0}; // 'NODES' IN AX25 FORMAT diff --git a/L4Code.c b/L4Code.c index 6f19652..f9741cd 100644 --- a/L4Code.c +++ b/L4Code.c @@ -42,7 +42,7 @@ VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session); VOID SENDL4DISC(TRANSPORTENTRY * Session); int C_Q_COUNT(VOID * Q); TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK); -VOID InformPartner(struct _LINKTABLE * LINK); +VOID InformPartner(struct _LINKTABLE * LINK, int Reason); VOID IFRM150(TRANSPORTENTRY * Session, PDATAMESSAGE Buffer); VOID SendConNAK(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG); BOOL FINDCIRCUIT(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY ** REQL4, int * NewIndex); @@ -416,7 +416,7 @@ int GETBUSYBIT(TRANSPORTENTRY * L4) return L4->NAKBITS; } -VOID Q_IP_MSG(PDATAMESSAGE Buffer) +VOID Q_IP_MSG(MESSAGE * Buffer) { if (IPHOSTVECTOR.HOSTAPPLFLAGS & 0x80) { @@ -1084,7 +1084,7 @@ VOID ProcessIframe(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer) if (Buffer->PID == 0xCC || Buffer->PID == 0xCD) { - Q_IP_MSG(Buffer); + Q_IP_MSG((MESSAGE *)Buffer); return; } @@ -1100,7 +1100,7 @@ VOID ProcessIframe(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer) { // MUST KILL SESSION - InformPartner(LINK); // CLOSE IT + InformPartner(LINK, NORMALCLOSE); // CLOSE IT LINK->CIRCUITPOINTER = NULL; // AND UNHOOK } LINK->LINKTYPE = 3; // NOW WE KNOW ITS A CROSSLINK @@ -1721,7 +1721,7 @@ VOID FRAMEFORUS(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, int ApplMask, if (L3MSG->L4ID == 0x0C && L3MSG->L4INDEX == 0x0C) { - Q_IP_MSG((PDATAMESSAGE)L3MSG); + Q_IP_MSG((MESSAGE *)L3MSG); return; } diff --git a/LinBPQ.c b/LinBPQ.c index 0ea99b0..514c131 100644 --- a/LinBPQ.c +++ b/LinBPQ.c @@ -190,8 +190,8 @@ BOOL ChatInit(); VOID CloseChat(); VOID CloseTNCEmulator(); -config_t cfg; -config_setting_t * group; +static config_t cfg; +static config_setting_t * group; BOOL MonBBS = TRUE; BOOL MonCHAT = TRUE; @@ -257,6 +257,26 @@ int Slowtimer = 0; #define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer int ReportTimer = 0; +// Console Terminal Support + +struct ConTermS +{ + int BPQStream; + BOOL Active; + int Incoming; + + char kbbuf[INPUTLEN]; + int kbptr; + + char * KbdStack[MAXSTACK]; + int StackIndex; + + BOOL CONNECTED; + int SlowTimer; +}; + +struct ConTermS ConTerm = {0, 0}; + VOID CheckProgramErrors() { @@ -486,7 +506,7 @@ int SESSHDDRLEN = 0; UCHAR MCOM; UCHAR MUIONLY; UCHAR MTX; -unsigned long long MMASK; +uint64_t MMASK; UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list @@ -516,7 +536,178 @@ UCHAR * GetLogDirectory() { return LogDirectory; } -extern int POP3Timer; +extern int POP3Timer; + +// Console Terminal Stuff + +#ifndef WIN32 + +#define _getch getchar + +/** + Linux (POSIX) implementation of _kbhit(). + Morgan McGuire, morgan@cs.brown.edu + */ + +#include +#include +#include +#include + +int _kbhit() { + static const int STDIN = 0; + static int initialized = 0; + + if (! initialized) { + // Use termios to turn off line buffering + struct termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = 1; + } + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; +} + +#endif + +void ConTermInput(char * Msg) +{ + int i; + + if (ConTerm.BPQStream == 0) + { + ConTerm.BPQStream = FindFreeStream(); + + if (ConTerm.BPQStream == 255) + { + ConTerm.BPQStream = 0; + printf("No Free Streams\n"); + return; + } + } + + if (!ConTerm.CONNECTED) + SessionControl(ConTerm.BPQStream, 1, 0); + + ConTerm.StackIndex = 0; + + // Stack it + + if (ConTerm.KbdStack[19]) + free(ConTerm.KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + ConTerm.KbdStack[i+1] = ConTerm.KbdStack[i]; + } + + ConTerm.KbdStack[0] = _strdup(ConTerm.kbbuf); + + ConTerm.kbbuf[ConTerm.kbptr]=13; + + SendMsg(ConTerm.BPQStream, ConTerm.kbbuf, ConTerm.kbptr+1); +} + +void ConTermPoll() +{ + int port, sesstype, paclen, maxframe, l4window, len; + int state, change, InputLen, count; + char callsign[11] = ""; + char Msg[300]; + + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + SessionState(ConTerm.BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) + { + // Connected + + ConTerm.CONNECTED = TRUE; + ConTerm.SlowTimer = 0; + } + else + { + ConTerm.CONNECTED = FALSE; + printf("*** Disconnected\n"); + } + } + + GetMsg(ConTerm.BPQStream, Msg, &InputLen, &count); + + if (InputLen) + { + char * ptr = Msg; + char * ptr2 = ptr; + Msg[InputLen] = 0; + + while (ptr) + { + ptr2 = strlop(ptr, 13); + + // Replace CR with CRLF + + printf(ptr); + + if (ptr2) + printf("\r\n"); + + ptr = ptr2; + } + } + + if (_kbhit()) + { + unsigned char c = _getch(); + + if (c == 0xe0) + { + // Cursor control + + c = _getch(); + + if (c == 75) // cursor left + c = 8; + } + +#ifdef WIN32 + printf("%c", c); +#endif + if (c == 8) + { + if (ConTerm.kbptr) + ConTerm.kbptr--; + printf(" \b"); // Already echoed bs - clear typed char from screen + return; + } + + if (c == 13 || c == 10) + { + ConTermInput(ConTerm.kbbuf); + ConTerm.kbptr = 0; + return; + } + + ConTerm.kbbuf[ConTerm.kbptr++] = c; + fflush(NULL); + + } + + return; + +} + + +int Redirected = 0; int main(int argc, char * argv[]) { @@ -556,6 +747,12 @@ int main(int argc, char * argv[]) prctl(PR_SET_DUMPABLE, 1); // Enable Core Dumps even with setcap #endif #endif + + // Disable Console Terminal if stdout redirected + + if (!isatty(STDOUT_FILENO)) + Redirected = 1; + #endif printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); @@ -1214,6 +1411,9 @@ int main(int argc, char * argv[]) FreeSemaphore(&Semaphore); + if (Redirected == 0) + ConTermPoll(); + if (NUMBEROFTNCPORTS) TNCTimer(); diff --git a/MailDataDefs.c b/MailDataDefs.c index ea3825d..4c177d9 100644 --- a/MailDataDefs.c +++ b/MailDataDefs.c @@ -219,3 +219,4 @@ char ** HoldTo; // Hold on TO Call char ** HoldAt; // Hold on AT Call char ** HoldBID; // Hold on BID +struct ConsoleInfo * ConsHeader[2]; \ No newline at end of file diff --git a/MailNode.vcproj.DESKTOP-TGEL8RC.John.user b/MailNode.vcproj.DESKTOP-TGEL8RC.John.user index 7a49481..6975522 100644 --- a/MailNode.vcproj.DESKTOP-TGEL8RC.John.user +++ b/MailNode.vcproj.DESKTOP-TGEL8RC.John.user @@ -11,7 +11,7 @@ {3766AA10-C777-4ED8-A83D-F1452DE9B666} MailNode Win32Proj - 10.0.17763.0 @@ -68,23 +67,20 @@ <_ProjectFileVersion>15.0.28307.799 - $(SolutionDir)$(Configuration)\ - C:\Dev\Msdev2005\Intermed\LinBPQ\$(Configuration)\ + C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ true true - C:\Dev\Msdev2005\Intermed\LinBPQ\$(Configuration)\ - c:\LinBPQ - $(SolutionDir)$(Configuration)\ - C:\Dev\Msdev2005\Intermed\LinBPQ\$(Configuration)\ + C:\Dev\Msdev2005\$(SolutionName)\$(ProjectName)\$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(ProjectName)\$(Configuration)\ false false - C:\Dev\Msdev2005\Intermed\LinBPQ\$(Configuration)\ @@ -99,7 +95,7 @@ EditAndContinue - kernel32.lib;WS2_32.Lib;C:\Dev\Msdev2005\Projects\BPQ32\CKernel\Debug\libconfig.lib;DbgHelp.lib;setupapi.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) + kernel32.lib;WS2_32.Lib;..\lib\libconfigd.lib;DbgHelp.lib;setupapi.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) c:\LINBPQ\$(ProjectName).exe true true @@ -120,10 +116,10 @@ Level3 - EditAndContinue + ProgramDatabase - kernel32.lib;WS2_32.Lib;C:\Dev\Msdev2005\Projects\BPQ32\CKernel\x64\Debug\libconfig.lib;DbgHelp.lib;setupapi.lib;C:\Users\John\OneDrive\Dev\Source\miniupnpc-2.2.3\msvc\x64\Debug\miniupnpc.lib;zlibstat64.lib;%(AdditionalDependencies) + kernel32.lib;WS2_32.Lib;C:\OneDrive\Dev\Source\bpq32\libconfig\x64\Release\libconfig.lib;DbgHelp.lib;setupapi.lib;C:\OneDrive\Dev\Source\miniupnpc-2.2.3\msvc\x64\Debug\miniupnpc.lib;C:\Users\johnw\Downloads\zlib-1.2.11-binaries-x64-release\zlib-1.2.11\binaries\x64\Release\zlib.lib;%(AdditionalDependencies) c:\LINBPQ\$(ProjectName).exe true true @@ -131,7 +127,7 @@ Console 4000000 0 - false + false @@ -144,7 +140,7 @@ ProgramDatabase - kernel32.lib;WS2_32.Lib;..\lib\libconfig.lib;DbgHelp.lib;Setupapi.lib;%(AdditionalDependencies) + kernel32.lib;WS2_32.Lib;..\lib\libconfig.lib;DbgHelp.lib;Setupapi.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) c:\devprogs\bpq32\LinBPQ.exe true Console @@ -158,7 +154,7 @@ ..\CKernel;..\CommonSource;..\CInclude;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;LINBPQ;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;LINBPQ;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) MultiThreaded @@ -166,7 +162,7 @@ ProgramDatabase - kernel32.lib;WS2_32.Lib;..\lib\libconfig.lib;DbgHelp.lib;Setupapi.lib;%(AdditionalDependencies) + kernel32.lib;WS2_32.Lib;..\lib\libconfig.lib;DbgHelp.lib;Setupapi.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) c:\devprogs\bpq32\LinBPQ.exe true Console @@ -215,6 +211,7 @@ + @@ -288,7 +285,6 @@ - diff --git a/MailNode.vcxproj.filters b/MailNode.vcxproj.filters new file mode 100644 index 0000000..23094c6 --- /dev/null +++ b/MailNode.vcxproj.filters @@ -0,0 +1,324 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/MailNode.vcxproj.user b/MailNode.vcxproj.user new file mode 100644 index 0000000..c9aaeaf --- /dev/null +++ b/MailNode.vcxproj.user @@ -0,0 +1,8 @@ + + + + C:\Dev\Msdev2005\projects\bpq32\BPQMail\x64\Debug\LinBPQ.exe + c:\linbpq + WindowsLocalDebugger + + \ No newline at end of file diff --git a/MailTCP.c b/MailTCP.c index b67b839..b26f6da 100644 --- a/MailTCP.c +++ b/MailTCP.c @@ -39,9 +39,8 @@ int CurrentSockets=0; #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -SOCKADDR_IN local_sin; /* Local socket - internet style */ - -PSOCKADDR_IN psin; +static SOCKADDR_IN local_sin; /* Local socket - internet style */ +static PSOCKADDR_IN psin; SOCKET smtpsock, pop3sock; diff --git a/Moncode.c b/Moncode.c index 47de2ce..762fd6d 100644 --- a/Moncode.c +++ b/Moncode.c @@ -60,7 +60,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #define NODES_SIG 0xFF char * strlop(char * buf, char delim); -UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, int msglen); +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, unsigned int msglen); char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen); UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen); @@ -145,7 +145,7 @@ int APRSDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, uint64_t Mask) { return IntDecodeFrame(msg, buffer, Stamp, Mask, TRUE, FALSE); } -DllExport int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, int Stamp) +DllExport int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp) { return IntDecodeFrame(msg, buffer, Stamp, MMASK, FALSE, FALSE); } diff --git a/MultiConsole.c b/MultiConsole.c index 8b8ec98..81b3530 100644 --- a/MultiConsole.c +++ b/MultiConsole.c @@ -31,6 +31,9 @@ BOOL FlashOnConnect; BOOL WrapInput; BOOL CloseWindowOnBye; +char RTFHeader[4000]; +int RTFHddrLen = 0; + RECT ConsoleRect; char chatMsg[] = "\rSysop wants to chat to you\r"; diff --git a/PG/Loop.c b/PG/Loop.c new file mode 100644 index 0000000..ac753d9 --- /dev/null +++ b/PG/Loop.c @@ -0,0 +1,24 @@ +#include +#include + +/* + * TST_PG.C + * + * Little test program of "PG" command for FBB BBS software. + * + * (C) F6FBB 1991. + * + * FBB software 5.14 and up. + * + * + * This program echoes to the user what he types + * or executes a BBS command preceded by "CMD" + * until "BYE" is received + */ + + +main(int argc, char **argv) +{ + Sleep(10000); + return 0; +} \ No newline at end of file diff --git a/PG/Loop.exe b/PG/Loop.exe new file mode 100644 index 0000000..1ddf86e Binary files /dev/null and b/PG/Loop.exe differ diff --git a/PG/Loop.obj b/PG/Loop.obj new file mode 100644 index 0000000..f9d68d3 Binary files /dev/null and b/PG/Loop.obj differ diff --git a/PG/PGTest.c b/PG/PGTest.c new file mode 100644 index 0000000..4d3742d --- /dev/null +++ b/PG/PGTest.c @@ -0,0 +1,53 @@ +#include + +/* + * TST_PG.C + * + * Little test program of "PG" command for FBB BBS software. + * + * (C) F6FBB 1991. + * + * FBB software 5.14 and up. + * + * + * This program echoes to the user what he types + * or executes a BBS command preceded by "CMD" + * until "BYE" is received + */ + + +main(int argc, char **argv) +{ + int i; + int level = atoi(argv[2]); /* Get level from argument list */ + + /* and transform it to integer */ + if (level == 0) { /* Is level equal to 0 ? */ + /* This is the first call */ + printf("Hello %s, type BYE when you want to stop !\n", argv[1]); + return(1); /* program will be called again */ + } + else { + strupr(argv[5]); /* Capitalise the first word */ + if (strcmp(argv[5], "BYE") == 0) { /* is BYE received ? */ + printf("Ok, bye-bye\n"); + return(0); /* Yes, go on BBS */ + } + else if (strcmp(argv[5], "CMD") == 0) { /* is CMD received ? */ + for (i = 6 ; i < argc ; i++) /* List line arguments */ + printf("%s ", argv[i]); /* sent by user */ + putchar('\n'); + for (i = 6 ; i < argc ; i++) /* List line arguments */ + printf("%s ", argv[i]); /* sent by user */ + putchar('\n'); + return(4); /* Yes, send command */ + } + else { + printf("You told me : "); /* These are other lines */ + for (i = 5 ; i < argc ; i++) /* List line arguments */ + printf("%s ", argv[i]); /* sent by user */ + putchar('\n'); + return(1); /* No, call again program */ + } + } +} \ No newline at end of file diff --git a/PG/PGTest.exe b/PG/PGTest.exe new file mode 100644 index 0000000..7f01106 Binary files /dev/null and b/PG/PGTest.exe differ diff --git a/PG/PGTest.obj b/PG/PGTest.obj new file mode 100644 index 0000000..c97914c Binary files /dev/null and b/PG/PGTest.obj differ diff --git a/RigControl.c b/RigControl.c index 090816a..9b33f57 100644 --- a/RigControl.c +++ b/RigControl.c @@ -896,10 +896,10 @@ int Rig_CommandEx(struct RIGPORTINFO * PORT, struct RIGINFO * RIG, TRANSPORTENTR { if (RIG->NumberofBands) { - RIG->ScanStopped &= (0xffffffff ^ (1 << Port)); + RIG->ScanStopped &= (0xffffffffffffffff ^ ((uint64_t)1 << Port)); if (Session != (TRANSPORTENTRY *) -1) // Used for internal Stop/Start - RIG->ScanStopped &= 0xfffffffe; // Clear Manual Stopped Bit + RIG->ScanStopped &= 0xfffffffffffffffe; // Clear Manual Stopped Bit if (n > 2) RIG->ScanCounter = atoi(Mode) * 10; //Start Delay @@ -926,7 +926,7 @@ int Rig_CommandEx(struct RIGPORTINFO * PORT, struct RIGINFO * RIG, TRANSPORTENTR if (_stricmp(FreqString, "SCANSTOP") == 0) { - RIG->ScanStopped |= (1 << Port); + RIG->ScanStopped |= ((uint64_t)1 << Port); if (Session != (TRANSPORTENTRY *) -1) // Used for internal Stop/Start RIG->ScanStopped |= 1; // Set Manual Stopped Bit @@ -2800,7 +2800,7 @@ void CheckRX(struct RIGPORTINFO * PORT) return; if (PORT->RXBuffer[Length-1] != 0xfd) - return; + return; // Echo ProcessICOMFrame(PORT, PORT->RXBuffer, Length); // Could have multiple packets in buffer diff --git a/SCSPactor.c b/SCSPactor.c index 8c2081b..61568fa 100644 --- a/SCSPactor.c +++ b/SCSPactor.c @@ -114,7 +114,7 @@ int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); -VOID PTCSuspendPort(struct TNCINFO * TNC); +VOID PTCSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC); VOID PTCReleasePort(struct TNCINFO * TNC); int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); int CheckMode(struct TNCINFO * TNC); @@ -468,6 +468,14 @@ ok: // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + SCSCheckRX(TNC); SCSPoll(port); @@ -4147,7 +4155,7 @@ VOID CloseComplete(struct TNCINFO * TNC, int Stream) } -VOID PTCSuspendPort(struct TNCINFO * TNC) +VOID PTCSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; diff --git a/SCSTracker.c b/SCSTracker.c index 8ca38b4..caa6b0e 100644 --- a/SCSTracker.c +++ b/SCSTracker.c @@ -108,7 +108,7 @@ BOOL TrkWriteCommBlock(struct TNCINFO * TNC) } -VOID TRKSuspendPort(struct TNCINFO * TNC) +VOID TRKSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; @@ -367,6 +367,14 @@ ok: // 100 mS Timer. + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + // See if waiting for connect after changing MYCALL if (TNC->SlowTimer) diff --git a/SerialPort.c b/SerialPort.c index 713ab4e..59ab627 100644 --- a/SerialPort.c +++ b/SerialPort.c @@ -39,7 +39,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include "CHeaders.h" -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); #include "bpq32.h" @@ -722,7 +722,7 @@ VOID SerialReleaseTNC(struct TNCINFO * TNC) } -VOID SerialSuspendPort(struct TNCINFO * TNC) +VOID SerialSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { SerialSendCommand(TNC, "CONOK OFF\r"); } diff --git a/TNCEmulators.c b/TNCEmulators.c index b8fb2e2..966b785 100644 --- a/TNCEmulators.c +++ b/TNCEmulators.c @@ -55,7 +55,7 @@ VOID GETDATA(struct TNCDATA * TNC); VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream); VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream); VOID READCHANGE(int Stream); -VOID DOMONITORING(int NeedTrace); +VOID DOMONITORING(int NeedTrace, struct TNCDATA * TNC); int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp); time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count); BOOL TfPut(struct TNCDATA * TNC, UCHAR character); @@ -73,10 +73,11 @@ VOID DisableAppl(struct TNCDATA * TNC); int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay); int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen); int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen); +int SendHostOK(struct TNCDATA * TNC); extern struct TNCDATA * TNCCONFIGTABLE; -struct TNCDATA * TNC2TABLE; // malloc'ed +struct TNCDATA * TNC2TABLE = NULL; // malloc'ed extern int NUMBEROFTNCPORTS; // MODEFLAG DEFINITIONS @@ -229,7 +230,7 @@ typedef struct _SERIAL_STATUS { #ifndef WIN32 - + #ifdef MACBPQ #include #endif @@ -1214,7 +1215,7 @@ VOID TNCTRANS(struct TNCDATA * TNC, char * Tail, CMDX * CMD) TNCStream->MODEFLAG |= TRANS; TNCStream->MODEFLAG &= ~(COMMAND+CONV); } -static VOID RESTART(struct TNCDATA * TNC) +static VOID TNCRESTART(struct TNCDATA * TNC) { // REINITIALISE CHANNEL @@ -1242,12 +1243,12 @@ static VOID RESTART(struct TNCDATA * TNC) } -static VOID UNPROTOCMD(struct TNCDATA * TNC, char * Tail, CMDX * CMD) +static VOID TNCUNPROTOCMD(struct TNCDATA * TNC, char * Tail, CMDX * CMD) { } -CMDX COMMANDLIST[] = +CMDX TNCCOMMANDLIST[] = { "AUTOLF ",2, ONOFF, offsetof(struct TNCDATA, AUTOLF), "BBSMON ",6, ONOFF, offsetof(struct TNCDATA, BBSMON), @@ -1292,15 +1293,15 @@ CMDX COMMANDLIST[] = "PACLEN ",1,VALUE, offsetof(struct TNCDATA, TPACLEN), "PASS ",3,VALHEX, offsetof(struct TNCDATA, PASSCHAR), "RELEASE ",3,TNCRELEASE,0, - "RESTART ",7,RESTART,0, + "RESTART ",7,TNCRESTART,0, "TRANS ",1,TNCTRANS,0, - "UNPROTO ",1,UNPROTOCMD,0, + "UNPROTO ",1,TNCUNPROTOCMD,0, "USERS ",2,VALUE, offsetof(struct TNCDATA, Users), }; -static CMDX * CMD = NULL; -int NUMBEROFTNCCOMMANDS = sizeof(COMMANDLIST)/sizeof(CMDX); + +int NUMBEROFTNCCOMMANDS = sizeof(TNCCOMMANDLIST)/sizeof(CMDX); /*NEWVALUE DW 0 HEXFLAG DB 0 @@ -1449,9 +1450,13 @@ BOOL InitializeTNCEmulator() { // Com Port may be a hardware device (ie /dev/ttyUSB0) COMn or VCOMn (BPQ Virtual COM) - char * Baud = strlop(TNC->PORTNAME, ','); + char * Baud = strlop(TNC->PORTNAME, ':'); char * PNptr; + if (Baud == 0) + Baud = strlop(TNC->PORTNAME, ','); + + PNptr = &TNC->PORTNAME[0]; // Only show last element of name on Streams display @@ -1601,7 +1606,7 @@ BOOL InitializeTNCEmulator() TNC->Speed = atoi(Baud); else TNC->VCOM = TRUE; - + if (_memicmp(TNC->PORTNAME, "COM", 3) == 0) { TNC->VCOM = FALSE; @@ -1750,17 +1755,46 @@ VOID TNCTimer() struct TNCDATA * TNC = TNC2TABLE; struct StreamInfo * channel; int n; - int NeedTrace = 0; + struct TNCDATA * TTNC = 0; // First TNC2 while (TNC) { + if (TNC->LastDEDPollTime && (time(NULL) > TNC->LastDEDPollTime + 30)) // No polls for 30 secs + { + int Len = 0; + int Count; + + TNC->LastDEDPollTime = 0; + + Debugprintf("DED Host Application Lost"); + TNC->MODE = 0; + TNC->HOSTSTATE = 0; + + DisableAppl(TNC); + + // Clear Monitor Q + + GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); + + while (Len) + { + GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); + } + continue; + } + + if (TNC->Mode != TNC2) goto NotTNC2; - - NeedTrace |= TNC->TRACEFLAG; //SEE IF ANY PORTS ARE MONITORING - // CHECK FOR PACTIMER EXPIRY AND CMDTIME + if (TTNC == 0 && TNC->TNC2Stream[0]) // Save first TNC2 TNC + TTNC = TNC; + + + NeedTrace |= TNC->TRACEFLAG; //SEE IF ANY PORTS ARE MONITORING + + // CHECK FOR PACTIMER EXPIRY AND CMDTIME if (TNC->CMDTMR) { @@ -1809,7 +1843,7 @@ NotTNC2: for (n = 1; n <= TNC->HOSTSTREAMS; n++) { channel = TNC->Channels[n]; - + if (channel->CloseTimer) { channel->CloseTimer--; @@ -1820,7 +1854,8 @@ NotTNC2: NextTNC: TNC = TNC->Next; } - DOMONITORING(NeedTrace); + if (TTNC) // Only if we have TNC2 Streams + DOMONITORING(NeedTrace, TTNC); } /* @@ -2213,35 +2248,28 @@ void CheckForDataFromTerminal(struct TNCDATA * TNC) } -VOID DOMONITORING(int NeedTrace) +VOID DOMONITORING(int NeedTrace, struct TNCDATA * TTNC) { // IF ANY PORTS HAVE MONITOR ENABLED, SET MONITOR BIT ON FIRST PORT - struct TNCDATA * TNC = TNC2TABLE; // malloc'ed int Tracebit = 0, len, count, n; time_t Stamp; uint64_t SaveMMASK = MMASK; BOOL SaveMTX = MTX; BOOL SaveMCOM = MCOM; BOOL SaveMUI = MUIONLY; - int BPQStream = 0; + struct TNCDATA * TNC = TNC2TABLE; + int BPQStream = TTNC->TNC2Stream[0]->BPQPort; if (NeedTrace) Tracebit = 0x80; - if (TNC->Channels[0]) - BPQStream = TNC->Channels[0]->BPQStream; - else if (TNC->TNC2Stream[0]) - BPQStream = TNC->TNC2Stream[0]->BPQPort; - else if (TNC->BPQPort) - BPQStream = TNC->BPQPort; - if (BPQStream) { - if (TNC->CONOK) - SetAppl(BPQStream, TNC->APPLFLAGS | Tracebit, TNC->APPLICATION); + if (TTNC->CONOK && TTNC->Mode == TNC2) + SetAppl(BPQStream, TTNC->APPLFLAGS | Tracebit, TTNC->APPLICATION); else - SetAppl(BPQStream, TNC->APPLFLAGS | Tracebit, 0); + SetAppl(BPQStream, TTNC->APPLFLAGS | Tracebit, 0); } Stamp = GetRaw(BPQStream, (char *)&MONITORDATA, &len, &count); @@ -2250,7 +2278,7 @@ VOID DOMONITORING(int NeedTrace) return; len = DecodeFrame(&MONITORDATA, MONBUFFER, Stamp); - + while (TNC) { if (TNC->Mode == TNC2 && TNC->TRACEFLAG) @@ -2585,7 +2613,7 @@ VOID TNCCOMMAND(struct TNCDATA * TNC) ptr2 = ptr1; // Save - CMD = &COMMANDLIST[0]; + CMD = &TNCCOMMANDLIST[0]; n = 0; for (n = 0; n < NUMBEROFTNCCOMMANDS; n++) @@ -3198,7 +3226,7 @@ VOID DisableAppl(struct TNCDATA * TNC) { int i, Stream; - for (i = 1; i <= TNC->HOSTSTREAMS; i++) + for (i = 0; i <= TNC->HOSTSTREAMS; i++) { Stream = TNC->Channels[i]->BPQStream; @@ -3218,6 +3246,17 @@ VOID EnableAppl(struct TNCDATA * TNC) } } +VOID EnableDEDAppl(struct TNCDATA * TNC) +{ + int i; + + SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + SetAppl(TNC->Channels[i]->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); + } +} BOOL TfPut(struct TNCDATA * TNC, UCHAR character) { struct StreamInfo * Channel; @@ -3298,8 +3337,8 @@ CHARMODE: TNC->MODE = 1; TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; - EnableAppl(TNC); - + + EnableDEDAppl(TNC); return(TRUE); } @@ -3372,24 +3411,29 @@ NOTDATA: } - if (TNC->DEDTXBUFFER[0] == 1) - goto DUFFHOSTCMD; - // sprintf(msg,"DED CMD: Port %d CMD %c MSGCHANNEL %d\n", TNC->ComPort, TNC->TONODEBUFFER[0], MSGCHANNEL); // OutputDebugString(msg); if (_memicmp(TNC->DEDTXBUFFER, "QRES", 4 == 0)) - goto SENDHOSTOK; + return SendHostOK(TNC); switch (toupper(TNC->DEDTXBUFFER[0])) { - case 1: + case 1: // Recovery - goto DUFFHOSTCMD; + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (i=0; i < LBADCMDREPLY; i++) + { + PUTCHARx(TNC, BADCMDREPLY[i]); + } + + return TRUE; case 'G': - goto POLL; + PROCESSPOLL(TNC, Channel); + return TRUE; case 'I': goto ICMD; @@ -3399,20 +3443,95 @@ NOTDATA: TNC->MODE = TNC->DEDTXBUFFER[5] & 1; if (TNC->MODE) - EnableAppl(TNC); + EnableDEDAppl(TNC); else DisableAppl(TNC); - goto SENDHOSTOK; + return SendHostOK(TNC); case 'C': goto CCMD; case 'D': - goto DCMD; + + // DISCONNECT REQUEST + + Disconnect(Channel->BPQStream); + return SendHostOK(TNC); case 'L': - goto LCMD; + + // Poll + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 1); + + // GET STATE AND QUEUED BUFFERS + + if (TNC->MSGCHANNEL) + { + // Data Channel + + LocalSessionState(Channel->BPQStream, &State, &Change, FALSE); + + if (State == 0) + Work = '0'; + else + Work = '4'; // AX.25 STATE + + PUTCHARx(TNC, Change + '0'); // Status Messages + + PUTCHARx(TNC, ' '); + + // GET OTHER QUEUE COUNTS + + Count = RXCount(Channel->BPQStream); + + sprintf(WorkString, "%d", Count); // message count + + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + + // NOT SENT IS NUMBER ON OUR QUEUE, NOT ACKED NUMBER FROM SWITCH + + // SEE HOW MANY BUFFERS ATTACHED TO Q HEADER IN BX + + Count = 0;// C_Q_COUNT(Channel->Chan_TXQ); + + sprintf(WorkString, "%d", Count); // message count + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + + if (Count > 8) + Work = '8'; // Busy + + Count = CountFramesQueuedOnSession(L4); + + sprintf(WorkString, "%d", Count); // message count + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + + } + else + { + //SEE IF MONITORED FRAMES AVAILABLE + + TNC->LastDEDPollTime = time(NULL); + + if (MONCount(TNC->Channels[0]->BPQStream)) + Work = 0x31; + else + Work = 0x30; + + } + + + PUTCHARx(TNC, '0'); + PUTCHARx(TNC, ' '); + PUTCHARx(TNC, Work); + PUTCHARx(TNC, 0); + + return TRUE; case '@': goto ATCOMMAND; @@ -3425,14 +3544,52 @@ NOTDATA: case 'M': + // Support BPQ Extensions IUSC followed by optional port list + if (TNC->DEDTXBUFFER[1] == 'N') - goto DISABLEMONITOR; + TNC->TRACEFLAG = 0; + else + { + char * ptr, * ptr2; + int port; + uint64_t mask = 0; - goto ENABLEMONITOR; + + TNC->DEDTXBUFFER[TNC->MSGLENGTH] = 0; + ptr = strlop(TNC->DEDTXBUFFER, ' '); + _strupr(TNC->DEDTXBUFFER); + + if (strchr(TNC->DEDTXBUFFER, 'U')) + TNC->MUIONLY = 1; + + + // Ptocess Mask + + while (ptr && ptr[0]) + { + ptr2 = strlop(ptr, ','); + port = atoi(ptr); + + if (port) + { + mask |= ((uint64_t)1 << (port - 1)); + ptr = ptr2; + } + } + + if (mask) + TNC->MMASK = mask; + + TNC->TRACEFLAG = 0x80; + } + + SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); + return SendHostOK(TNC); + case 'K': case 'O': - goto SENDHOSTOK; + return SendHostOK(TNC); case 'V': // Vesrion @@ -3444,7 +3601,7 @@ NOTDATA: return TRUE; default: - goto SENDHOSTOK; + return SendHostOK(TNC); ATCOMMAND: @@ -3547,40 +3704,15 @@ ICMD: } ECMD: - goto SENDHOSTOK; + return SendHostOK(TNC); -DUFFHOSTCMD: - - PUTCHARx(TNC, TNC->MSGCHANNEL); - - for (i=0; i < LBADCMDREPLY; i++) - { - PUTCHARx(TNC, BADCMDREPLY[i]); - } - - return TRUE; - -ENABLEMONITOR: - - TNC->TRACEFLAG = 0x80; - goto MONCOM; - -DISABLEMONITOR: - - TNC->TRACEFLAG = 0; - -MONCOM: - - SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); - - goto SENDHOSTOK; CCMD: // CONNECT REQUEST if (TNC->MSGCHANNEL == 0) - goto SENDHOSTOK; // SETTING UNPROTO ADDR - JUST ACK IT + return SendHostOK(TNC); // SETTING UNPROTO ADDR - JUST ACK IT *TNC->DEDCURSOR = 0; @@ -3651,91 +3783,13 @@ REALCALL: // READCHANGE(Channel->BPQStream); // Suppress Connected to Switch - goto SENDHOSTOK; + return SendHostOK(TNC); } } - goto SENDHOSTOK; - -DCMD: - -// DISCONNECT REQUEST - - Disconnect(Channel->BPQStream); - - goto SENDHOSTOK; - -LCMD: - - PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL - PUTCHARx(TNC, 1); - -// GET STATE AND QUEUED BUFFERS - - if (TNC->MSGCHANNEL) - goto NORM_L; - -// TO MONITOR CHANNEL - -// SEE IF MONITORED FRAMES AVAILABLE - - if (MONCount(TNC->Channels[0]->BPQStream)) - Work = 0x31; - else - Work = 0x30; - - goto MON_L; - -NORM_L: - - LocalSessionState(Channel->BPQStream, &State, &Change, FALSE); - - if (State == 0) - Work = '0'; - else - Work = '4'; // AX.25 STATE - - PUTCHARx(TNC, Change + '0'); // Status Messages - - PUTCHARx(TNC, ' '); - -// GET OTHER QUEUE COUNTS - - Count = RXCount(Channel->BPQStream); - - sprintf(WorkString, "%d", Count); // message count - - PUTSTRING(TNC, WorkString); - PUTCHARx(TNC, ' '); - -// NOT SENT IS NUMBER ON OUR QUEUE, NOT ACKED NUMBER FROM SWITCH + return SendHostOK(TNC); -// SEE HOW MANY BUFFERS ATTACHED TO Q HEADER IN BX - - Count = 0;// C_Q_COUNT(Channel->Chan_TXQ); - - sprintf(WorkString, "%d", Count); // message count - PUTSTRING(TNC, WorkString); - PUTCHARx(TNC, ' '); - - if (Count > 8) - Work = '8'; // Busy - - Count = CountFramesQueuedOnSession(L4); - - sprintf(WorkString, "%d", Count); // message count - PUTSTRING(TNC, WorkString); - PUTCHARx(TNC, ' '); - -MON_L: - - PUTCHARx(TNC, '0'); - PUTCHARx(TNC, ' '); - PUTCHARx(TNC, Work); - PUTCHARx(TNC, 0); - - return TRUE; HOSTDATAPACKET: @@ -3763,7 +3817,7 @@ HOSTDATAPACKET: // C_Q_ADD(Channel->Chan_TXQ, COPYMSGTOBUFFERS()); // RETURNS EDI = FIRST (OR ONLY) FRAGMENT -// goto SENDHOSTOK; +// return SendHostOK(TNC); // MAKE SURE NODE ISNT BUSY @@ -3778,17 +3832,13 @@ HOSTDATAPACKET: // OK TO PASS TO NODE SENDENFORCINGPACLEN(Channel, TNC->DEDTXBUFFER, TNC->MSGLENGTH); - goto SENDHOSTOK; + return SendHostOK(TNC); SendUnproto: SendMsg(0, TXBUFFERPTR, TNC->MSGLENGTH); - goto SENDHOSTOK; + return SendHostOK(TNC); -POLL: - - PROCESSPOLL(TNC, Channel); - return TRUE; YCMD: @@ -3835,83 +3885,53 @@ YCMD: TNC->HOSTSTREAMS = Work; } - /* - - Why is this here? - { - int Len=0; - UCHAR Message[1000]; - - while (TNC->RXCOUNT > 0) - { - Message[Len++]= *(TNC->PUTPTR++); - - TNC->RXCOUNT--; - - if (TNC->PUTPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) - TNC->PUTPTR = (UCHAR *)&TNC->TOUSERBUFFER; - - if (Len > 900) - { - BPQSerialSendData(TNC, Message, Len); - Len = 0; - } - } - - if (Len > 0) - { - BPQSerialSendData(TNC, Message, Len); - } - } - _asm { - - popad - - - RET -*/ - -SENDHOSTOK: + return SendHostOK(TNC); +} +int SendHostOK(struct TNCDATA * TNC) +{ PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 0); // NOTHING DOING return TRUE; - } + int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) -{ - int PollType; - - // ASK SWITCH FOR STATUS CHANGE OR ANY RECEIVED DATA +{ + // DED Mode - ASK SWITCH FOR STATUS CHANGE OR ANY RECEIVED DATA - if (TNC->MSGLENGTH == 1) - goto GENERALPOLL; + if (TNC->MSGLENGTH == 1) // General Poll + { + if (STATUSPOLL(TNC, Channel)) + return TRUE; // Status was reported - PollType = 0; + if (DATAPOLL(TNC, Channel)) + return TRUE; // Data Sent -// HE'S BEING AWKWARD, AND USING SPECIFIC DATA/STATUS POLL + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING - if (TNC->TONODEBUFFER[1] == '0') - goto DATAONLY; + return TRUE; + } - STATUSPOLL(TNC, Channel); + // HE'S BEING AWKWARD, AND USING SPECIFIC DATA/STATUS POLL -GENERALPOLL: + if (TNC->TONODEBUFFER[1] == '0') // Data only + { + if (DATAPOLL(TNC, Channel)) + return TRUE; // Data Sent + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; + } + + // Must be Status only if (STATUSPOLL(TNC, Channel)) return TRUE; // Status was reported -DATAONLY: - - if (DATAPOLL(TNC, Channel)) - return TRUE; // Data Sent - - goto SENDHOSTOK; // NOTHING DOING - - -SENDHOSTOK: - PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 0); // NOTHING DOING @@ -3980,13 +4000,15 @@ and then the very next poll to channel 0 will get: */ + Decoded[Len] = 0; + iptr = strchr(&Decoded[10], ':'); // Info if present MONHEADER[0] = 4; // NO DATA FOLLOWS MONCURSOR = &MONHEADER[1]; if (strstr(Decoded, "NET/ROM") || strstr(Decoded, "NODES br") || strstr(Decoded, "INP3 RIF")) - pid = 0xcf; + return 0; //pid = 0xcf; else pid = 0xf0; @@ -4763,21 +4785,21 @@ nochange: return; } -int DOCOMMAND(struct TNCDATA * conn) +int DOCOMMAND(struct TNCDATA * TNC) { char Errbuff[500]; int i; // PROCESS NORMAL MODE COMMAND - sprintf(Errbuff, "BPQHOST Port %d Normal Mode CMD %s\n",conn->ComPort, conn->TONODEBUFFER); - OutputDebugString(Errbuff); + Debugprintf(Errbuff, "BPQHOST Port %d Normal Mode CMD %s\n",TNC->ComPort, TNC->TONODEBUFFER); + // IF ECHO ENABLED, ECHO IT - if (conn->ECHOFLAG) + if (TNC->ECHOFLAG) { - UCHAR * ptr = conn->TONODEBUFFER; + UCHAR * ptr = TNC->TONODEBUFFER; UCHAR c; do @@ -4786,36 +4808,35 @@ int DOCOMMAND(struct TNCDATA * conn) if (c == 0x1b) c = ':'; - PUTCHARx(conn, c); + PUTCHARx(TNC, c); } while (c != 13); } - if (conn->TONODEBUFFER[0] != 0x1b) + if (TNC->TONODEBUFFER[0] != 0x1b) goto NOTCOMMAND; // DATA IN NORMAL MODE - IGNORE - switch (toupper(conn->TONODEBUFFER[1])) + switch (toupper(TNC->TONODEBUFFER[1])) { case 'J': - if (conn->TONODEBUFFER[6] == 0x0d) - conn->MODE = 0; + if (TNC->TONODEBUFFER[6] == 0x0d) + TNC->MODE = 0; else - conn->MODE = conn->TONODEBUFFER[6] & 1; + TNC->MODE = TNC->TONODEBUFFER[6] & 1; - if (conn->MODE) - EnableAppl(conn); - else - DisableAppl(conn); - - if (conn->MODE) + if (TNC->MODE) { // send host mode ack // PUTCHARx(conn, 0); // PUTCHARx(conn, 0); - conn->CURSOR = (UCHAR *)&conn->TONODEBUFFER; + EnableDEDAppl(TNC); + } + else + { + DisableAppl(TNC); return 0; } @@ -4823,20 +4844,20 @@ int DOCOMMAND(struct TNCDATA * conn) case 'E': - conn->ECHOFLAG = conn->TONODEBUFFER[2] & 1; + TNC->ECHOFLAG = TNC->TONODEBUFFER[2] & 1; break; case 'I': { // Save call - char * Call = &conn->TONODEBUFFER[2]; + char * Call = &TNC->TONODEBUFFER[2]; - *(conn->CURSOR - 2) = 0; + *(TNC->CURSOR - 2) = 0; - for (i = 0; i <= conn->HOSTSTREAMS; i++) + for (i = 0; i <= TNC->HOSTSTREAMS; i++) { - strcpy(conn->Channels[i]->MYCall, Call); + strcpy(TNC->Channels[i]->MYCall, Call); } break;; @@ -4845,11 +4866,11 @@ int DOCOMMAND(struct TNCDATA * conn) // PARAMS COMMAND - RETURN FIXED STRING - PARAMPORT = conn->TONODEBUFFER[2]; + PARAMPORT = TNC->TONODEBUFFER[2]; for (i=0; i < LPARAMREPLY; i++) { - PUTCHARx(conn, PARAMREPLY[i]); + PUTCHARx(TNC, PARAMREPLY[i]); } break; @@ -4859,7 +4880,7 @@ int DOCOMMAND(struct TNCDATA * conn) // Return Channel Not Connected - PUTSTRING(conn, "* CHANNEL NOT CONNECTED *\r"); + PUTSTRING(TNC, "* CHANNEL NOT CONNECTED *\r"); default: @@ -4875,7 +4896,7 @@ int DOCOMMAND(struct TNCDATA * conn) NOTCOMMAND: - conn->CURSOR = (UCHAR *)&conn->TONODEBUFFER; + TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; return 0; @@ -4960,7 +4981,7 @@ int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) return TRUE; } - OutputDebugString("BPQHOST Mondata Flag Set with no data"); + Debugprintf("BPQHOST Mondata Flag Set with no data"); NOMONITOR: @@ -4968,7 +4989,7 @@ NOMONITOR: stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); - if (Len) + while (Len) { // Use Normal Decode, then reformat to DED standard @@ -4978,14 +4999,18 @@ NOMONITOR: BOOL SaveMUI = MUIONLY; unsigned char Decoded[1000]; - IntSetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, 0); + IntSetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, TNC->MUIONLY); Len = IntDecodeFrame(&MONITORDATA, Decoded, stamp, TNC->MMASK, FALSE, FALSE); IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); - if (Len) + if (Len) { - return ConvertToDEDMonFormat(TNC, Decoded, Len, &MONITORDATA); + if (ConvertToDEDMonFormat(TNC, Decoded, Len, &MONITORDATA)) + return 1; + + stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); } + } return 0; } diff --git a/TelnetV6.c b/TelnetV6.c index fb55720..4a76bc2 100644 --- a/TelnetV6.c +++ b/TelnetV6.c @@ -99,6 +99,9 @@ extern HWND ClientWnd, FrameWnd; extern HANDLE hInstance; static RECT Rect; + +LRESULT CALLBACK TelWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + #endif extern int REALTIMETICKS; @@ -126,9 +129,6 @@ static int ProcessLine(char * buf, int Port); VOID __cdecl Debugprintf(const char * format, ...); char * strlop(char * buf, char delim); -#ifndef LINBPQ -LRESULT CALLBACK TelWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -#endif int DisplaySessions(struct TNCINFO * TNC); int DoStateChange(int Stream); @@ -185,7 +185,9 @@ void DeleteTelnetLogFiles() int DeleteLogFile(char * Log) { - WIN32_FIND_DATA ffd; + + + WIN32_FIND_DATA ffd; char szDir[MAX_PATH]; char File[MAX_PATH]; @@ -290,7 +292,6 @@ int DeleteTelnetLogFiles() - void BuffertoNode(struct ConnectionInfo * sockptr, char * MsgPtr, int InputLen) { // Queue to Node. Data may arrive it large quatities, possibly exceeding node buffer capacity @@ -792,23 +793,12 @@ scanCTEXT: static int MaxStreams = 26; -struct TNCINFO * CreateTTYInfo(int port, int speed); -BOOL OpenConnection(int); -BOOL SetupConnection(int); -BOOL CloseConnection(struct TNCINFO * conn); -BOOL WriteCommBlock(struct TNCINFO * TNC); -BOOL DestroyTTYInfo(int port); void CheckRX(struct TNCINFO * TNC); VOID TelnetPoll(int Port); VOID ProcessTermModeResponse(struct TNCINFO * TNC); VOID DoTNCReinit(struct TNCINFO * TNC); VOID DoTermModeTimeout(struct TNCINFO * TNC); -VOID ProcessPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID ProcessKPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); -VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); -VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); @@ -1672,6 +1662,7 @@ BOOL OpenSockets(struct TNCINFO * TNC) SOCKET OpenSocket6(struct TNCINFO * TNC, int port) { + struct sockaddr_in6 local_sin; /* Local socket - internet style */ struct sockaddr_in6 * psin; SOCKET sock; @@ -2774,7 +2765,7 @@ LRESULT CALLBACK TelWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara // struct ConnectionInfo * ConnectionInfo; - for (i=1; i<33; i++) + for (i=1; i <= MAXBPQPORTS; i++) { TNC = TNCInfo[i]; if (TNC == NULL) @@ -3122,7 +3113,6 @@ LRESULT CALLBACK TelWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara return DefMDIChildProc(hWnd, message, wParam, lParam); } - #endif int Socket_Accept(struct TNCINFO * TNC, SOCKET SocketId, int Port) diff --git a/UIARQ.c b/UIARQ.c index 05eface..6fdc5e3 100644 --- a/UIARQ.c +++ b/UIARQ.c @@ -25,7 +25,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include "CHeaders.h" -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); diff --git a/UZ7HODrv.c b/UZ7HODrv.c index 119d48e..90732a2 100644 --- a/UZ7HODrv.c +++ b/UZ7HODrv.c @@ -339,8 +339,13 @@ BOOL UZ7HOStartPort(struct PORTCONTROL * PORT) -VOID UZ7HOSuspendPort(struct TNCINFO * TNC) +VOID UZ7HOSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { + // We don't want to suspend port if on same TNC + + if (MasterPort[TNC->Port] == MasterPort[ThisTNC->Port]) + return; + TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; RegisterAPPLCalls(TNC, TRUE); } @@ -2315,6 +2320,16 @@ GotStream: if (_stricmp(RXHeader->callto, Appl) == 0) break; + + memcpy(Appl, APPL->APPLALIAS_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(RXHeader->callto, Appl) == 0) + break; + } if (App < 32) diff --git a/V4.c b/V4.c index 106446f..5e26ca9 100644 --- a/V4.c +++ b/V4.c @@ -40,7 +40,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include "tncinfo.h" #include "bpq32.h" -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); #define WSA_ACCEPT WM_USER + 1 #define WSA_DATA WM_USER + 2 diff --git a/VARA.c b/VARA.c index 1cccc44..cf6dd7d 100644 --- a/VARA.c +++ b/VARA.c @@ -34,7 +34,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include #endif -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); #define SD_RECEIVE 0x00 @@ -341,10 +341,17 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) case 7: // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; // Check session limit timer - if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) { if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) @@ -970,7 +977,7 @@ static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) return Len; } -VOID VARASuspendPort(struct TNCINFO * TNC) +VOID VARASuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { VARASendCommand(TNC, "LISTEN OFF\r", TRUE); } diff --git a/Versions.h b/Versions.h index 74a69fb..fe9fcb6 100644 --- a/Versions.h +++ b/Versions.h @@ -10,8 +10,8 @@ #endif -#define KVers 6,0,23,71 -#define KVerstring "6.0.23.71\0" +#define KVers 6,0,23,76 +#define KVerstring "6.0.23.76\0" #ifdef CKernel diff --git a/WINMOR.c b/WINMOR.c index 33dd3a6..8bb62ae 100644 --- a/WINMOR.c +++ b/WINMOR.c @@ -76,8 +76,8 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include #endif -int (WINAPI FAR *GetModuleFileNameExPtr)(); -int (WINAPI FAR *EnumProcessesPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); #define SD_RECEIVE 0x00 @@ -1339,7 +1339,7 @@ VOID SuspendOtherPorts(struct TNCINFO * ThisTNC) if (rxInterlock == 0 || txInterlock == 0) return; - for (i=1; i<33; i++) + for (i = 1; i <= MAXBPQPORTS; i++) { TNC = TNCInfo[i]; if (TNC == NULL) @@ -1350,7 +1350,7 @@ VOID SuspendOtherPorts(struct TNCINFO * ThisTNC) if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group if (TNC->SuspendPortProc) - TNC->SuspendPortProc(TNC); + TNC->SuspendPortProc(TNC, ThisTNC); } } @@ -1366,7 +1366,7 @@ VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC) if (rxInterlock == 0 && txInterlock == 0) return; - for (i=1; i<33; i++) + for (i=1; i <= MAXBPQPORTS; i++) { TNC = TNCInfo[i]; if (TNC == NULL) @@ -1381,7 +1381,7 @@ VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC) } } -VOID WinmorSuspendPort(struct TNCINFO * TNC) +VOID WinmorSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { if (TNC->CONNECTED) send(TNC->TCPSock, "CODEC FALSE\r\n", 14, 0); diff --git a/WinRPR.c b/WinRPR.c index 391edc9..f12971e 100644 --- a/WinRPR.c +++ b/WinRPR.c @@ -34,7 +34,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include "CHeaders.h" -int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); #include "tncinfo.h" @@ -98,7 +98,7 @@ BOOL KAMStartPort(struct PORTCONTROL * PORT); BOOL KAMStopPort(struct PORTCONTROL * PORT); -extern VOID TRKSuspendPort(struct TNCINFO * TNC); +extern VOID TRKSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC); extern VOID TRKReleasePort(struct TNCINFO * TNC); extern VOID CloseDebugLogFile(int Port); extern BOOL OpenDebugLogFile(int Port); @@ -495,6 +495,14 @@ ok: // 100 mS Timer. + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + // See if waiting for connect after changing MYCALL if (TNC->SlowTimer) diff --git a/asmstrucs.h b/asmstrucs.h index 5d11572..29dcb64 100644 --- a/asmstrucs.h +++ b/asmstrucs.h @@ -730,6 +730,9 @@ typedef struct KISSINFO struct KISSINFO * POLLPOINTER; // LAST GROUP POLLED int POLLED; // SET WHEN POLL RECEIVED + UCHAR * KISSCMD; // Commands to be sent when port opened + int KISSCMDLEN; + // UCHAR WIN32INFO[16]; // FOR WINDOWS DRIVER } *PKISSINFO; @@ -1158,6 +1161,7 @@ struct TNCDATA BOOL MCON; // TRACE MODE FLAGS BOOL MCOM; BOOL MALL; + BOOL MUIONLY; BOOL AUTOLF; // Add LF after CR BOOL BBSMON; // SPECIAL SHORT MONITOR FOR BBS BOOL MTX; // MONITOR TRANSMITTED FRAMES @@ -1210,6 +1214,8 @@ struct TNCDATA int MONLENGTH; int MONFLAG; + time_t LastDEDPollTime; // To detect lost host program + // Kantronics Fields int RXBPtr; diff --git a/bpqaxip.c b/bpqaxip.c index d4f6579..690f96d 100644 --- a/bpqaxip.c +++ b/bpqaxip.c @@ -1344,7 +1344,7 @@ LRESULT FAR PASCAL ConfigWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lPa BOOL UDPFlag, BCFlag; struct AXIPPORTINFO * PORT; - for (i=1; i<33; i++) + for (i=1; i <= MAXBPQPORTS; i++) { PORT = Portlist[i]; if (PORT == NULL) @@ -1648,7 +1648,7 @@ LRESULT CALLBACK MHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam int i; - for (i=1; i<33; i++) + for (i=1; i <= MAXBPQPORTS; i++) { PORT = Portlist[i]; if (PORT == NULL) diff --git a/bpqchat.c b/bpqchat.c index 04e350d..6739c64 100644 --- a/bpqchat.c +++ b/bpqchat.c @@ -198,6 +198,10 @@ char WindowSize[32]; extern config_setting_t * group; +char RTFHeader[4000]; +int RTFHddrLen; + + int ProgramErrors = 0; // Forward declarations of functions included in this code module: @@ -1255,9 +1259,8 @@ int RefreshMainWindow() #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -SOCKADDR_IN local_sin; /* Local socket - internet style */ - -PSOCKADDR_IN psin; +static SOCKADDR_IN local_sin; /* Local socket - internet style */ +static PSOCKADDR_IN psin; SOCKET sock; diff --git a/bpqchat.h b/bpqchat.h index db46fd3..955ac48 100644 --- a/bpqchat.h +++ b/bpqchat.h @@ -483,9 +483,8 @@ typedef struct user_t #define MAXLINES 1000 #define LINELEN 200 -char RTFHeader[4000]; - -int RTFHddrLen; +extern char RTFHeader[4000]; +extern int RTFHddrLen; struct ConsoleInfo { diff --git a/bpqmail.h b/bpqmail.h index eb7323a..ab1052f 100644 --- a/bpqmail.h +++ b/bpqmail.h @@ -350,6 +350,17 @@ struct B2RestartData int Count; // Give up if too many restarts }; +//------ TAJ ----- +typedef struct PGARGS +{ + CIRCUIT * conn; + struct UserInfo * user; + char InputBuffer[80]; + int Len; +}RUNPGARGS, *RUNPGARGS_PTR; + +//--------------- + #pragma pack(1) struct TempUserInfo @@ -375,7 +386,11 @@ struct TempUserInfo int LLCount; // Number still to send in List Last N int UpdateLatest; // if set, save last listed as latest BOOL IncludeKilled; // Show Killed Messages if SYSOP - + //--- TAJ --- + int PG_INDEX; // current index of PG server + int PG_SERVER; // PG server to run + RUNPGARGS_PTR RUNPGPARAMS; // pointer to RUNPGARGS for dealloc + //----------- }; #define PMSG 1 @@ -759,9 +774,8 @@ struct FBBHeaderLine #define MAXLINES 1000 #define LINELEN 200 -char RTFHeader[4000]; - -int RTFHddrLen; +extern char RTFHeader[4000]; +extern int RTFHddrLen; struct ConsoleInfo { @@ -1577,7 +1591,7 @@ extern BOOL WarnNoRoute; extern BOOL SendPtoMultiple; extern BOOL Localtime; -struct ConsoleInfo * ConsHeader[2]; +extern struct ConsoleInfo * ConsHeader[2]; extern BOOL NeedHomeBBS; extern char ConfigName[250]; diff --git a/bpqmail.h.bak b/bpqmail.h.bak new file mode 100644 index 0000000..a153c3f --- /dev/null +++ b/bpqmail.h.bak @@ -0,0 +1,1604 @@ +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#define LIBCONFIG_STATIC +#include + +#include "compatbits.h" + +#ifndef LINBPQ +#include "bpq32.h" +#include "BPQMailrc.h" +#include "dbghelp.h" +#else +#include "CHeaders.h" +#endif + +#include "asmstrucs.h" + + +#define NEWROUTING + + + +// Standard __except handler for try/except + +VOID CheckProgramErrors(); +VOID WriteMiniDump(); + +extern int ProgramErrors; + +extern struct _EXCEPTION_POINTERS exinfox; + +#ifdef WIN32 +Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg); + +#define My__except_Routine(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + CheckProgramErrors();\ + WriteMiniDump();\ +} + + +/* +#define My__except_Routine(Message) \ +__except(memcpy(&exinfox, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Dump_Process_State(&exinfox, Message);\ + CheckProgramErrors();\ +} + +#define My__except_RoutineWithDisconnect(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + FreeSemaphore(&ChatSemaphore);\ + if (conn->BPQStream < 0)\ + CloseConsole(conn->BPQStream);\ + else\ + Disconnect(conn->BPQStream);\ +} +*/ +#define My_except_RoutineWithDiscBBS(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + if (conn->BPQStream < 0)\ + CloseConsole(conn->BPQStream);\ + else\ + Disconnect(conn->BPQStream);\ + CheckProgramErrors();\ +} +#endif +#define MAXUSERNAMELEN 6 + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_CONNECT WM_USER + 2 +#define WSA_DATA WM_USER + 3 +#define NNTP_ACCEPT WM_USER + 4 +#define NNTP_DATA WM_USER + 5 + +#ifdef _DEBUG + +VOID * _malloc_dbg_trace(int len, int type, char * file, int line); + +#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _recalloc(p, c, s) _recalloc_dbg(p, c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define free(p) _free_dbg(p, _NORMAL_BLOCK) +#define _strdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) + + +#define zalloc(s) _zalloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#else +#define zalloc(s) _zalloc(s) +#endif + +#ifdef LINBPQ + +#undef zalloc +#define zalloc _zalloc + +#endif + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line); + +#define LOG_BBS 0 +#define LOG_CHAT 1 +#define LOG_TCP 2 +#define LOG_DEBUG_X 3 + + +//Chat Duplicate suppression Code + +#define MAXDUPS 10 // Number to keep +#define DUPSECONDS 5 // TIme to Keep + +struct DUPINFO +{ + time_t DupTime; + char DupUser[10]; + char DupText[100]; +}; + + +struct UserRec +{ + char * Callsign; + char * UserName; + char * Password; +}; + + + +typedef struct ConnectionInfo_S +{ + struct ConnectionInfo_S *next; + PROC *proc; + UCHAR RadioOnlyMode; // T or R flag for Radio Only mode. + + int Number; // Number of record - for Connections display + BOOL Active; + int BPQStream; + int paclen; + UCHAR Callsign[11]; // Station call including SSID + BOOL GotHeader; + UCHAR InputMode; // Line by Line or Binary or YAPP + + UCHAR * InputBuffer; + int InputBufferLen; + int InputLen; // Data we have already = Offset of end of an incomplete packet; + + struct UserInfo * UserPointer; + int Retries; + int LoginState; // 1 = user ok, 2 = password ok + int Flags; + + // Data to the user is kept in a malloc'd buffer. This can be appended to, + // and data sucked out under both terminal and system flow control. PACLEN is + // enfored when sending to node. + + UCHAR * OutputQueue; // Messages to user + int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. + + int CloseAfterFlush; // Close session when all sent. Set to 100ms intervals to wait. + + int ErrorCount; // Invalid Command count + BOOL Paging; // Set if user wants paging + int LinesSent; // Count when paging + int PageLen; // Lines per page + + UCHAR * MailBuffer; // Mail Message being received + UCHAR * CopyBuffer; // Mail Message being forwarded + int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct + + long lastmsg; // Last Listed. Stored here, updated in user record only on clean close + BOOL sysop; // Set if user is authenticated as a sysop + BOOL Secure_Session; // Set if Local Terminal, or Telnet connect with SYSOP status + UINT BBSFlags; // Set if defined as a bbs and SID received + struct MsgInfo * TempMsg; // Header while message is being received + struct MsgInfo * FwdMsg; // Header while message is being forwarded + + char ** To; // May be several Recipients + int ToCount; + + int BBSNumber; // The BBS number (offset into bitlist of BBSes to forward a message to + int NextMessagetoForward; // Next index to check in forward cycle + BOOL BPQBBS; // Set if SID indicates other end is BPQ + char MSGTYPES[20]; // Any MSGTYPEFLAGS + BOOL SendT; // Send T messages + BOOL SendP; // Send P messages + BOOL SendB; // Send Bulls + BOOL SendWL2KFW; // send ;FW: + int MaxBLen; // Max Size for this session + int MaxPLen; // Max Size for this session + int MaxTLen; // Max Size for this session + BOOL DoReverse; // Request Reverse Forward + char LastForwardType; // Last type of messages forwarded + struct FBBHeaderLine * FBBHeaders; // The Headers from an FFB forward block + char FBBReplyChars[36]; //The +-=!nnnn chars for the 5 proposals + int FBBReplyIndex; // current Reply Pointer + int FBBIndex; // current propopsal number + int RestartFrom; // Restart position + BOOL NeedRestartHeader; // Set if waiting for 6 byte restart header + BOOL DontSaveRestartData; // Set if corrupt data received + BOOL FBBMsgsSent; // Messages need to be maked as complete when next command received + UCHAR FBBChecksum; // Header Checksum + BOOL OpenBCM; // OpenBCM mode (escape -xFF chars) + BOOL InTelnetExcape; // Last Char was 0xff + BOOL LocalMsg; // Set if current Send command is for a local user + BOOL NewUser; // Set if first time user has accessed BBS + BOOL Paclink; // Set if receiving messages from Paclink + BOOL RMSExpress; // Set if receiving messages from RMS Express + BOOL WL2K; // Set if communicating with a CMS + BOOL PAT; // Set if communicating with PAT + char ** PacLinkCalls; // Calls we are getting messages for + BOOL SkipPrompt; // Set if a remote node sends a > at the end of his CTEXT + BOOL SkipConn; // Node sends "connected" in its CTEXT + int Watchdog; // Hung Circuit Detect. + int SessType; // BPQ32 sesstype bits + +#define Sess_L2LINK 1 +#define Sess_SESSION 2 +#define Sess_UPLINK 4 +#define Sess_DOWNLINK 8 +#define Sess_BPQHOST 0x20 +#define Sess_PACTOR 0x40 + + HANDLE DebugHandle; // File Handle for session-based debugging + + char ARQFilename[256]; // Filename from ARQ:FILE:: Header + int ARQClearCount; // To make sure queues are flushed when sending + + int SIDResponseTimer; // Used to detect incomplete handshake + + char PQChallenge[20]; // Secure User logon challange + char SecureMsg[20]; // CMS Secure Signon Response + int MCastListenTime; // Time to run session for + + int YAPPLen; // Bytes sent/received of YAPP Message + long YAPPDate; // Date for received file - if set enables YAPPC + + int SyncCompressedLen; + int SyncXMLLen; + int SyncMsgLen; + char * SyncHost; // Saved so can send "request sync" + int SyncPort; + UCHAR * SyncMessage; // Compressed SYNC message to send + + // These are used to detect CRLF split over a packet boundary + int usingCR; // Session is (normally) using CR as terminator + int lastLineEnd; // Terminator for current line + + struct ConnectionInfo_S * SysopChatStream; // Stream sysop is chatting to + +} ConnectionInfo, CIRCUIT; + +// Flags Equates + +#define GETTINGUSER 1 +#define GETTINGBBS 2 +#define CHATMODE 4 +#define GETTINGTITLE 8 +#define GETTINGMESSAGE 16 +#define CHATLINK 32 // Link to another Chat Node +#define SENDTITLE 64 +#define SENDBODY 128 +#define WAITPROMPT 256 // Waiting for prompt after message +#define PROPOSINGSYNCMSG 512 // Sent proposal to SYNC, waiting response +#define SENDINGSYNCMSG 1024 // Sent message to SYNC, waiting response +#define REQUESTINGSYNC 2048 +#define GETTINGSYNCMESSAGE 4096 // Receiving body of a SYNC message + +// BBSFlags Equates + +#define BBS 1 +#define FBBForwarding 2 +#define FBBCompressed 4 +#define FBBB1Mode 8 +#define FBBB2Mode 16 +#define RunningConnectScript 32 +#define MBLFORWARDING 64 // MBL Style Frwarding- waiting for OK/NO or Prompt following message +#define TEXTFORWARDING 128 // Plain Text forwarding +#define OUTWARDCONNECT 256 // We connected to them +#define FLARQMODE 512 // Message from FLARQ +#define FLARQMAIL 1024 // Sending FLARQ Format Message +#define ARQMAILACK 2048 // Waiting for all data to be acked +#define NEEDLF 4096 // Add LF to forward script commands (fro Telnet +#define MCASTRX 8192 // Stream in Multicast RX Mode +#define DISCONNECTING 16384 // Disconnect sent to Node +#define YAPPTX 0x008000 // Sending YAPP file +#define SYSOPCHAT 0x010000 // Chatting to BBS console +#define WINLINKRO 0x020000 // WL2K RO (no J in SID) +#define SYNCMODE 0x040000 // RMS RELAY SYNC +#define MFJMODE 0x080000 // MFJ PMS +#define NEWPACCOM 0x100000 // PACCOM PMS 3.2 +#define SETCALLTOSENDER 0x200000 // Set calling call to message sender + + +struct FBBRestartData +{ + struct MsgInfo * TempMsg; // Header while message is being received + struct UserInfo * UserPointer; + UCHAR * MailBuffer; // Mail Message being received + int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct + int Count; // Give up if too many restarts +}; + +// We need to keep the B2Message file for B2 messages we are sending until the messages is acked, so +// we can restart it. Otherwise the file may change, resulting in a checksum error + + +struct B2RestartData +{ + int CSize; // Compresses Size (B2 proto) + UCHAR * CompressedMsg; // Compressed Body fo B2 + struct MsgInfo * FwdMsg; + struct UserInfo * UserPointer; + int Count; // Give up if too many restarts +}; + +#pragma pack(1) + +struct TempUserInfo +{ + int LastAuthCode; // Protect against playback attack + + // Fields used to allow interrupting and resuming a paged listing + + BOOL ListActive; // Doing a list + BOOL ListSuspended; // Paused doing a list + int LastListedInPagedMode; + char LastListCommand[80]; + char LastListParams[80]; + int LinesSent; + char SendFullFrom; + char ListType; + char ListDirn; + char ListStatus; + char ListSelector; // < > @ etc + + int ListRangeStart; + int ListRangeEnd; + int LLCount; // Number still to send in List Last N + int UpdateLatest; // if set, save last listed as latest + BOOL IncludeKilled; // Show Killed Messages if SYSOP + +}; + +#define PMSG 1 +#define BMSG 2 +#define TMSG 3 + +struct OldUserInfo +{ + // Old format - without message type specific traffic counts + + char Call[10]; // Connected call without SSID +// indicat relai[8]; /* 64 Digis path */ + int lastmsg; /* 4 Last L number */ + int ConnectsIn; /* 4 Number of connexions in*/ + int TimeLastConnected; //Last connexion date */ +// long lastyap __a2__ ; /* 4 Last YN date */ + ULONG flags ; /* 4 Flags */ + + UCHAR PageLen; // Lines Per Page + UCHAR lang ; /* 1 Language */ + + int Xnewbanner; /* 4 Last Banner date */ + short Xdownload ; /* 2 download size (KB) = 100 */ + char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) + char BBSNumber; // BBS Bitmap Index Number + struct BBSForwardingInfo * ForwardingInfo; + struct UserInfo * BBSNext; // links BBS record + struct TempUserInfo * Temp; // Working Fields - not saved in user file + char xfree[6]; /* 6 Spare */ + char Xtheme; /* 1 Current topic */ + + char Name[18]; /* 18 1st Name */ + char Address[61]; /* 61 Address */ + + // Stats. Was City[31]; /* 31 City */ + + int MsgsReceived; + int MsgsSent; + int MsgsRejectedIn; // Messages we reject + int MsgsRejectedOut; // Messages Rejectd by other end + int BytesForwardedIn; + int BytesForwardedOut; + int ConnectsOut; // Forwarding Connects Out + + USHORT RMSSSIDBits; // SSID's to poll in RMS + + char Spare1; + + char HomeBBS[41]; /* 41 home BBS */ + char QRA[7]; /* 7 Qth Locator */ + char pass[13]; /* 13 Password */ + char ZIP[9]; /* 9 Zipcode */ + BOOL spare; +} ; /* Total : 360 bytes */ + +struct MsgStats +{ + int ConnectsIn; /* 4 Number of connexions in*/ + int ConnectsOut; // Forwarding Connects Out + + // Stats saveed by message type + + int MsgsReceived[4]; + int MsgsSent[4]; + int MsgsRejectedIn[4]; // Messages we reject + int MsgsRejectedOut[4]; // Messages Rejectd by other end + int BytesForwardedIn[4]; + int BytesForwardedOut[4]; +}; + +struct UserInfo +{ + // New Format - with stats maintained by message type and unused fields removed. + + char Call[10]; // Connected call without SSID + + int Length; // To make subsequent format changes easier + + int lastmsg; /* 4 Last L number */ + int xTimeLastConnected; //Last connexion date */ + ULONG flags ; /* 4 Flags */ + + UCHAR PageLen; // Lines Per Page + + char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) + unsigned char BBSNumber; // BBS Bitmap Index Number + struct BBSForwardingInfo * ForwardingInfo; + struct UserInfo * BBSNext; // links BBS record + struct TempUserInfo * Temp; // Working Fields - not saved in user file + char Name[18]; /* 18 1st Name */ + char Address[61]; /* 61 Address */ + + USHORT RMSSSIDBits; // SSID's to poll in RMS + + char HomeBBS[41]; /* 41 home BBS */ + char QRA[7]; /* 7 Qth Locator */ + char pass[13]; /* 13 Password */ + char ZIP[9]; /* 9 Zipcode */ + + struct MsgStats Total; + struct MsgStats Last; + + char CMSPass[16]; // For Secure Signon + int WebSeqNo; + + long long TimeLastConnected; //Last connection date */ + + char Filler[44 - 8]; // So we can add a few fields wirhout another resize +}; + +// flags equates + +#define F_Excluded 0x0001 +#define F_GGG 0x0002 +#define F_Expert 0x0004 +#define F_SYSOP 0x0008 +#define F_BBS 0x0010 +#define F_RMSREDIRECT 0x0020 +#define F_BBB 0x0040 +#define F_CCC 0x0080 +#define F_DDD 0x0100 +#define F_EEE 0x0200 +#define F_FFF 0x0400 +#define F_PMS 0x0800 +#define F_EMAIL 0x1000 +#define F_HOLDMAIL 0x2000 +#define F_POLLRMS 0x4000 +#define F_SYSOP_IN_LM 0x8000 +#define F_Temp_B2_BBS 0x00010000 // "Winlink Express User" +#define F_NOWINLINK 0x00020000 // Don't add Winlink.org +#define F_NOBULLS 0x00040000 +#define F_NTSMPS 0x00080000 +#define F_APRSMFOR 0x00100000 // Send APRS message for new mail +#define F_APRSSSID 0xF0000000 // (Top 4 Bits + + +struct Override +{ + char * Call; + int Days; +}; + +struct ALIAS +{ + char * Alias; + char * Dest; +}; + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGEX * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + UCHAR DIGIS[56]; // Padding in case we have digis + +}MESSAGEX, *PMESSAGEX; + + +#pragma pack() + +// Message Database Entry. Designed to be compatible with FBB + +#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy +#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists. + +#pragma pack(1) + +struct OldMsgInfo +{ + char type ; + char status ; + int number ; + int length ; + int datereceived; + char bbsfrom[7] ; // ? BBS we got it from ? + char via[41] ; + char from[7] ; + char to[7] ; + char bid[13] ; + char title[61] ; + char bin; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; + + char free[4]; + unsigned short nblu; + int theme ; + time_t datecreated ; + time_t datechanged ; + char fbbs[10] ; + char forw[10] ; + char emailfrom[41]; +} ; + + +struct MsgInfo +{ + char type; + char status; + int number; + int length; + int xdatereceived; + char bbsfrom[7]; // ? BBS we got it from ? + char via[41]; + char from[7]; + char to[7]; + char bid[13]; + char title[61]; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; + + #define B2Msg 1 // Set if Message File is a formatted B2 message + #define Attachments 2 // Set if B2 message has attachments + #define FromPaclink 4 + #define FromCMS 8 + #define FromRMSExpress 16 + #define RadioOnlyMsg 32 // Received using call-T + #define RadioOnlyFwd 64 // Received using call-R + + int xdatecreated; + int xdatechanged; + UCHAR fbbs[NBMASK]; + UCHAR forw[NBMASK]; + char emailfrom[41]; + char Locked; // Set if selected for sending (NTS Pickup) + char Defered; // FBB response '=' received + UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP) + +// For 64 bit time_t compatibility define as long long +// (so struct is same with 32 or 64 bit time_t) + + long long datereceived; + long long datecreated; + long long datechanged; + + char Spare[61 - 24]; // For future use +} ; + +#define MSGTYPE_B 0 +#define MSGTYPE_P 1 + +#define MSGSTATUS_N 0 +#define MSGSTATUS_Y 1 +#define MSGSTATUS_F 2 +#define MSGSTATUS_K 3 +#define MSGSTATUS_H 4 +#define MSGSTATUS_D 5 +#define MSGSTATUS_$ 6 + +struct NNTPRec +{ + // Used for NNTP access to Bulls + + struct NNTPRec * Next; // Record held in chain, so can be held sorted + char NewsGroup[64]; // = Bull TO.at field + int FirstMsg; // Lowest Number + int LastMsg; // Highest Number + int Count; // Active Msgs + time_t DateCreated; // COntains Creation Date of First Bull in Group +}; + + +typedef struct { + char mode; + char BID[13]; + union + { /* array named screen */ + struct + { + unsigned short msgno; + unsigned short timestamp; + }; + CIRCUIT * conn; + } u; +} BIDRec, *BIDRecP; + + +typedef struct WPDBASE{ /* 194 bytes */ + char callsign[7]; + char name[13]; + unsigned char Type; + unsigned char changed; + unsigned short seen; + long long last_modif; + long long last_seen; + char first_homebbs[41]; + char secnd_homebbs[41]; + char first_zip[9]; + char secnd_zip[9]; + char first_qth[31]; + char secnd_qth[31]; +} WPRec, * WPRecP; + +#pragma pack() + +struct FWDBAND +{ + time_t FWDStartBand; + time_t FWDEndBand; +}; + + + +struct BBSForwardingInfo +{ + // Holds info for forwarding + + BOOL Enabled; // Forwarding Enabled + char ** ConnectScript; // Main Connect Script + char ** TempConnectScript; // Used with FWD command. + int ScriptIndex; // Next line in script + BOOL MoreLines; // Set until script is finsihed + + char ** TOCalls; // Calls in to field + char ** ATCalls; // Calls in ATBBS field + char ** HaddressesP; // Heirarchical Addresses for Personals to forward to (as stored) + char *** HADDRSP; // Heirarchical Addresses for Personals to forward to + char ** Haddresses; // Heirarchical Addresses to forward to (as stored) + char *** HADDRS; // Heirarchical Addresses to forward to + int * HADDROffet; // Elements added to complete the HR. At least n+1 must match to forward + char ** FWDTimes; // Time bands to forward + struct FWDBAND ** FWDBands; + int MsgCount; // Messages for this BBS + BOOL ReverseFlag; // Set if BBS wants to poll for reverse forwarding + BOOL Forwarding; // Forward in progress + int MaxFBBBlockSize; + BOOL AllowBlocked; // Allow FBB Blocked + BOOL AllowCompressed; // Allow FBB Compressed + BOOL AllowB1; // Enable B1 + BOOL AllowB2; // Enable B2 + BOOL SendCTRLZ; // Send Ctrl/z instead of /ex + BOOL PersonalOnly; // Only Forward Personals + BOOL SendNew; // Forward new messages immediately + int FwdInterval; + int RevFwdInterval; + int FwdTimer; + time_t LastReverseForward; + char *BBSHA; // HA of BBS + char ** BBSHAElements; // elements of HA of BBS + int ConTimeout; +// char UserCall[10]; // User we are forwarding on behalf of (Currently only for RMS) +// int UserIndex; // index of User we are forwarding on behalf of (Currently only for RMS) +}; + + +struct FBBHeaderLine +{ + // Holds the info from the (up to) 5 headers presented at the start of a Forward Block + + char Format; // Ascii or Binary + char MsgType; // P B etc + char From[7]; // Sender + char ATBBS[41]; // BBS of recipient (@ Field) + char To[7]; // Recipient + char BID[13]; + int Size; + int CSize; // Compresses Size (B2 proto) + BOOL B2Message; // Set if an FC type + UCHAR * CompressedMsg; // Compressed Body fo B2 + struct MsgInfo * FwdMsg; // Header so we can mark as complete +}; + +#define MAXSTACK 20 +//#define MAXLINE 10000 +#define INPUTLEN 512 + +#define MAXLINES 1000 +#define LINELEN 200 + +char RTFHeader[4000]; + +int RTFHddrLen; + +struct ConsoleInfo +{ + struct ConsoleInfo * next; + CIRCUIT * Console; + int BPQStream; + WNDPROC wpOrigInputProc; + HWND hConsole; + HWND hwndInput; + HWND hwndOutput; + HMENU hMenu; // handle of menu + RECT ConsoleRect; + RECT OutputRect; + + int Height, Width, LastY; + + int ClientHeight, ClientWidth; + char kbbuf[INPUTLEN]; + int kbptr; + + char * readbuff; // Malloc'ed + int readbufflen; // Current Length + char * KbdStack[MAXSTACK]; + + int StackIndex; + + BOOL Bells; + BOOL FlashOnBell; // Flash instead of Beep + BOOL StripLF; + + BOOL WarnWrap; + BOOL FlashOnConnect; + BOOL WrapInput; + BOOL CloseWindowOnBye; + + unsigned int WrapLen; + int WarnLen; + int maxlinelen; + + int PartLinePtr; + int PartLineIndex; // Listbox index of (last) incomplete line + + DWORD dwCharX; // average width of characters + DWORD dwCharY; // height of characters + DWORD dwClientX; // width of client area + DWORD dwClientY; // height of client area + DWORD dwLineLen; // line length + int nCaretPosX; // horizontal position of caret + int nCaretPosY; // vertical position of caret + + COLORREF FGColour; // Text Colour + COLORREF BGColour; // Background Colour + COLORREF DefaultColour; // Default Text Colour + + int CurrentLine; // Line we are writing to in circular buffer. + + int Index; + BOOL SendHeader; + BOOL Finished; + + char OutputScreen[MAXLINES][LINELEN]; + + int Colourvalue[MAXLINES]; + int LineLen[MAXLINES]; + + int CurrentColour; + int Thumb; + int FirstTime; + BOOL Scrolled; // Set if scrolled back + int RTFHeight; // Height of RTF control in pixels + +}; + + +struct MSESSION +{ + struct MSESSION * Next; + unsigned int Key; + char * FileName; + char * OrigTimeStamp; + unsigned char * Message; + int MessageLen; + unsigned char * BlockList; + char * ID; + int BlockSize; + int BlockCount; + int BlocksReceived; + BOOL Completed; + time_t Created; + time_t LastUpdated; + int Index; // Line in Display +}; + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...); +char * strlop(char * buf, char delim); +int rt_cmd(CIRCUIT *circuit, char * Buffer); +CIRCUIT *circuit_new(CIRCUIT *circuit, int flags); +VOID BBSputs(CIRCUIT * conn, char * buf); +VOID FBBputs(CIRCUIT * conn, char * buf); +void makelinks(void); +VOID * _zalloc(size_t len); +VOID FreeChatMemory(); +VOID ChatTimer(); +VOID nputs(CIRCUIT * conn, char * buf); +VOID node_close(); +VOID removelinks(); +VOID SetupChat(); +VOID SendChatLinkStatus(); +VOID ClearChatLinkStatus(); +VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len); + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); + +// TCP Connections. FOr the moment SMTP or POP3 + +typedef struct SocketConnectionInfo +{ + struct SocketConnectionInfo * Next; + int Number; // Number of record - for Connections display + SOCKET socket; + SOCKADDR_IN sin; + int Type; // SMTP or POP3 + BOOL AMPR; // Set if sending to an AMPR.ORG server + char FromDomain[50]; // Domain we are sending from + struct UserInfo * bbs; // BBS dor forwarding to AMPR + int State; // Transaction State Machine + UCHAR CallSign[10]; + UCHAR TCPBuffer[3000]; // For converting byte stream to messages + int InputLen; // Data we have alreasdy = Offset of end of an incomplete packet; + + char * MailFrom; // Envelope Sender and Receiver + char ** RecpTo; // May be several Recipients + int Recipients; + + UCHAR * MailBuffer; // Mail Message being received. malloc'ed as needed + int MailBufferSize; // Total Malloc'ed size. Actual size is in MailSize + int MailSize; + int Flags; + + struct UserInfo * POP3User; + struct MsgInfo ** POP3Msgs; // Header List of messages for this uaer + int POP3MsgCount; // No of Messages + int POP3MsgNum; // Sequence number of message being received + + struct MsgInfo * SMTPMsg; // message for this SMTP connection + + UCHAR * SendBuffer; // Message being sent if socket is busy. malloc'ed as needed + int SendBufferSize; // Total Malloc'ed size. Actual size is in MailSize + int SendSize; // Bytes in buffer + int SendPtr; // next byte to send when ready + + struct NNTPRec * NNTPGroup; // Currently Selected Group + int NNTPNum; // Currenrly Selected Msg Number + int Timeout; // Used to close a session that is open too long + +} SocketConn; + +typedef struct KEYVALUES +{ + char * Key; + char * Value; +} KeyValues; + +typedef struct WEBMAILINFO +{ + // Info for HTML Forms Processing + + struct HtmlFormDir * Dir; // HTML Directory + char * txtFileName; // Template Name for current message + char * InputHTMLName; // Template to input message + char * DisplayHTMLName; // Template to display message + char * ReplyHTMLName; // Template for replying to message + char * txtFile; // Template data + char * OrigTo; // To field when template loaded + char * OrigSubject; // Subject field when template loaded + char * OrigBody; // Msg text when template loaded + char * OrigBID; + char OrigType; + char * To; + char * CC; + char * Subject; + char * Body; + char * BID; + char Type; + struct MsgInfo * Msg; // Msg record if replying + KeyValues txtKeys[1000]; // Key/Value pairs for txt template. Used when creating or displaying + KeyValues XMLKeys[1000]; // Key/Value pairs from XML attachment + BOOL isReply; + char * XMLName; + char * XML; // XML attachment + int XMLLen; + int Files; + char * FileName[100]; // Attachments + char * FileBody[100]; + int FileLen[100]; + + char * Header; + int HeaderLen; + + char * Footer; + int FooterLen; + + char * Reply; // put in here to save passing lots of parameters + int * RLen; + + BOOL Winlink; + BOOL P2P; + BOOL Packet; + + int CurrentMessageIndex; // Index of message currently displayed (for Prev and Next) + +}WebMailInfo; + +#define SMTPServer 1 +#define POP3SLAVE 2 +#define SMTPClient 3 +#define POP3Client 4 +#define NNTPServer 5 + +// State Values + +#define GettingUser 1 +#define GettingPass 2 +#define Authenticated 4 + +#define Connecting 8 + +// SMTP Master + +#define WaitingForGreeting 16 +#define WaitingForHELOResponse 32 +#define WaitingForFROMResponse 64 +#define WaitingForTOResponse 128 +#define WaitingForDATAResponse 256 +#define WaitingForBodyResponse 512 +#define WaitingForAUTHResponse 1024 + +// POP3 Master + +#define WaitingForUSERResponse 32 +#define WaitingForPASSResponse 64 +#define WaitingForSTATResponse 128 +#define WaitingForUIDLResponse 256 +#define WaitingForLISTResponse 512 +#define WaitingForRETRResponse 512 +#define WaitingForDELEResponse 1024 +#define WaitingForQUITResponse 2048 + + +#define SE 240 // End of subnegotiation parameters +#define NOP 241 //No operation +//#define DM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. +#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. +#define IP 244 //Suspend Interrupt or abort the process to which the NVT is connected. +#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. +#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. +#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. +#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. +#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. +#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. +#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. +#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. +#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. +#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. +#define IAC 255 + +#define suppressgoahead 3 //858 +//#define Status 5 //859 +//#define echo 1 //857 +#define timingmark 6 //860 +#define terminaltype 24 //1091 +#define windowsize 31 //1073 +#define terminalspeed 32 //1079 +#define remoteflowcontrol 33 //1372 +#define linemode 34 //1184 +#define environmentvariables 36 //1408 + +BOOL Initialise(); +#ifdef WIN32 +INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +#endif +int DisplaySessions(); +int DoStateChange(int Stream); +int DoReceivedData(int Stream); +int DoBBSMonitorData(int Stream); +int Connected(int Stream); +int Disconnected(int Stream); +//int Socket_Accept(int SocketId); +//int Socket_Data(int SocketId,int error, int eventcode); +int DataSocket_Read(SocketConn * sockptr, SOCKET sock); +int DataSocket_Write(SocketConn * sockptr, SOCKET sock); +int DataSocket_Disconnect(SocketConn * sockptr); +int RefreshMainWindow(); +int Terminate(); +int SendtoSocket(SOCKET sock,char * Msg); +int WriteLog(char * msg); +int ConnectState(int Stream); +UCHAR * EncodeCall(UCHAR * Call); +int ParseIniFile(char * fn); +struct UserInfo * AllocateUserRecord(char * Call); +struct MsgInfo * AllocateMsgRecord(); +BIDRec * AllocateBIDRecord(); +BIDRec * AllocateTempBIDRecord(); +struct UserInfo * LookupCall(char * Call); +BIDRec * LookupBID(char * BID); +BIDRec * LookupTempBID(char * BID); +VOID RemoveTempBIDS(CIRCUIT * conn); +VOID SaveUserDatabase(); +VOID GetUserDatabase(); +VOID GetMessageDatabase(); +VOID SaveMessageDatabase(); +VOID GetBIDDatabase(); +VOID SaveBIDDatabase(); +VOID GetWPDatabase(); +VOID CopyWPDatabase(); +VOID SaveWPDatabase(); +VOID GetBadWordFile(); +WPRec * LookupWP(char * Call); +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user); +VOID ProcessLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID ProcessChatLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user); +int QueueMsg( ConnectionInfo * conn, char * msg, int len); +VOID SendUnbuffered(int stream, char * msg, int len); +//int GetFileList(char * Dir); +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp); +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void DoKillCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context); +void DoReadCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call); +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call); +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB); +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); +int GetUserMsg(int m, char * Call, BOOL SYSOP); +void Flush(ConnectionInfo * conn); +VOID ClearQueue(ConnectionInfo * conn); +void TrytoSend(); +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); +struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop); +char * ReadMessageFile(int msgno); +char * ReadInfoFile(char * File); +char * FormatDateAndTime(time_t Datim, BOOL DateOnly); +int CriticalErrorHandler(char * error); +BOOL DoSendCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +BOOL CreateMessage(ConnectionInfo * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title); +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg); +int ProcessConnecting(CIRCUIT * circuit, char * Buffer, int Len); +VOID SaveConfig(char * ConfigName); +BOOL GetConfig(char * ConfigName); +int GetIntValue(config_setting_t * group, char * name); +BOOL GetStringValue(config_setting_t * group, char * name, char * value); +BOOL GetConfigFromRegistry(); +VOID Parse_SID(CIRCUIT * conn, char * SID, int len); +VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len); +VOID ProcessFBBLine(ConnectionInfo * conn, struct UserInfo * user, UCHAR * Buffer, int len); +VOID SetupNextFBBMessage(CIRCUIT * conn); +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); +int PrintMessages(HWND hDlg, int Count, int * Indexes); +int check_fwd_bit(char *mask, int bbsnumber); +void set_fwd_bit(char *mask, int bbsnumber); +void clear_fwd_bit (char *mask, int bbsnumber); +VOID SetupForwardingStruct(struct UserInfo * user); +BOOL Forward_Message(struct UserInfo * user, struct MsgInfo * Msg); +VOID StartForwarding (int BBSNumber, char ** TempScript); +BOOL Reverse_Forward(); +int ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len); +BOOL FBBDoForward(CIRCUIT * conn); +BOOL FindMessagestoForward(CIRCUIT * conn); +BOOL SeeifMessagestoForward(int BBSNumber, CIRCUIT * Conn); +int CountMessagestoForward(struct UserInfo * user); + +VOID * GetMultiLineDialogParam(HWND hDialog, int DLGItem); + +#define LIBCONFIG_STATIC +#include "libconfig.h" +VOID * GetMultiStringValue(config_setting_t * hKey, char * ValueName); +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName); + +int MultiLineDialogToREG_MULTI_SZ(HWND hWnd, int DLGItem, HKEY hKey, char * ValueName); +int Do_BBS_Sel_Changed(HWND hDlg); +VOID FreeForwardingStruct(struct UserInfo * user); +VOID FreeList(char ** Hddr); +int Do_User_Sel_Changed(HWND hDlg); +int Do_Msg_Sel_Changed(HWND hDlg); +VOID Do_Save_Msg(); +VOID Do_Add_User(HWND hDlg); +VOID Do_Delete_User(HWND hDlg); +VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user); +VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user); +VOID Do_Save_User(HWND hDlg, BOOL ShowBox); +VOID DeleteBBS(); +VOID AddBBS(); +VOID SaveBBSConfig(); +BOOL GetChatConfig(); +VOID SaveChatConfig(); +VOID SaveISPConfig(); +VOID SaveFWDConfig(); +VOID SaveMAINTConfig(); +VOID SaveWelcomeMsgs(); +VOID SavePrompts(); +VOID ReinitializeFWDStruct(struct UserInfo * user); +VOID CopyBIDDatabase(); +VOID CopyMessageDatabase(); +VOID CopyUserDatabase(); +VOID FWDTimerProc(); +VOID CreateMessageFromBuffer(CIRCUIT * conn); +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...); +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...); +VOID FreeOverrides(); +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length); +struct UserInfo * FindRMS(); +VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo); +BOOL ConnecttoBBS (struct UserInfo * user); +BOOL SetupNewBBS(struct UserInfo * user); +VOID CreateRegBackup(); +VOID SaveFilters(HWND hDlg); +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type); +BOOL CheckHoldFilters(char * From, char * To, char * ATBBS, char * BID); +BOOL CheckifLocalRMSUser(char * FullTo); +VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Context); +BOOL wildcardcompare(char * Target, char * Match); +VOID SendWarningToSYSOP(struct MsgInfo * Msg); +VOID DoEditUserCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoPollRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoShowRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoSetIdleTime(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoFwdCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID SaveFwdParams(char * Call, struct BBSForwardingInfo * ForwardingInfo); +VOID DoAuthCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +VOID DoReroute(CIRCUIT * conn, struct UserInfo * user); + +// FBB Routines + +VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg); +VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); +VOID UnpackFBBBinary(CIRCUIT * conn); +void Decode(CIRCUIT * conn, __int16 DecodeOnly); +//long Encode(char * in, char * out, long inlen, BOOL B1Protocol); + +BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline); +VOID SaveFBBBinary(CIRCUIT * conn); +BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); +BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); + +// Console Routines + +BOOL CreateConsole(int Stream); +int WritetoConsoleWindow(int Stream, char * Msg, int len); +int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item); +void CopyRichTextToClipboard(HWND hWnd); +void CopyToClipboard(HWND hWnd); +VOID CloseConsole(int Stream); + +// Monitor Routines + +BOOL CreateMonitor(); +int WritetoMonitorWindow(char * Msg, int len); + +BOOL CreateDebugWindow(); +VOID WritetoDebugWindow(char * Msg, int len); +VOID ClearDebugWindow(); +int RemoveLF(char * Message, int len); + +// Utilities + +BOOL isdigits(char * string); +void GetSemaphore(struct SEM * Semaphore, int ID); +void FreeSemaphore(struct SEM * Semaphore); + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...); + +VOID SortBBSChain(); +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG); +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup); + +// TCP Routines + +BOOL InitialiseTCP(); +VOID SetupListenSet(); +VOID TCPTimer(); +VOID TCPFastTimer(); +int Socket_Data(int sock, int error, int eventcode); +static int Socket_Accept(SOCKET SocketId); +int Socket_Connect(SOCKET sock, int Error); +VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len); +int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int Msglen, BOOL B2Flag); +BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg); +SOCKET CreateListeningSocket(int Port); +int TidyString(char * MailFrom); +VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len); +char *str_base64_encode(char *str); +int b64decode(char *str); +SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody); +BOOL POP3Connect(char * Host, int Port); +VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len); +VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len); +int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag); +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); +int SendSock(SocketConn * sockptr, char * msg); +VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...); +VOID SendFromQueue(SocketConn * sockptr); +VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes); +int CountMessagesTo(struct UserInfo * user, int * Unread); + +BOOL SendtoISP(); + +// NNTP ROutines + +VOID InitialiseNNTP(); +VOID BuildNNTPList(struct MsgInfo * Msg); +int NNTP_Data(int sock, int error, int eventcode); +int NNTP_Accept(SOCKET SocketId); + +VOID * GetOverrides(config_setting_t * group, char * ValueName); +VOID * RegGetOverrides(HKEY hKey, char * ValueName); + +VOID DoHouseKeeping(BOOL Mainual); +VOID ExpireMessages(); +VOID KillMsg(struct MsgInfo * Msg); +BOOL RemoveKilledMessages(); +VOID Renumber_Messages(); +BOOL ExpireBIDs(); +VOID MailHousekeepingResults(); +VOID CreateBBSTrafficReport(); +VOID CreateWPReport(); + +// WP Routines + +VOID ProcessWPMsg(char * MailBuffer, int Size, char * FisrtRLine); +VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime); +VOID UpdateWPWithUserInfo(struct UserInfo * user); +VOID GetWPBBSInfo(char * Rline); + +// UI Routines + +VOID SetupUIInterface(); +VOID Free_UI(); +VOID SendLatestUI(int Port); +VOID SendMsgUI(struct MsgInfo * Msg); +static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue); +VOID SeeifBBSUIFrame(struct _MESSAGEX * buff, int len); +struct MsgInfo * FindMessageByNumber(int msgno); +int CountConnectionsOnPort(int CheckPort); + +// Message Routing Routtines + +VOID SetupHAElements(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupMyHA(); +VOID SetupFwdAliases(); +struct Continent * FindContinent(char * Name); +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn); +BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo); +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); +BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); +BOOL CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); +BOOL CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); +int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo); +int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); + +VOID ReRouteMessages(); + +VOID initUTF8(); +int Is8Bit(unsigned char *cpt, int len); +int IsUTF8(unsigned char *ptr, int len); +int IsUTF8(unsigned char *ptr, int len); +int WebIsUTF8(unsigned char *ptr, int len); +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int TrytoGuessCode(unsigned char * Char, int Len); + + +VOID FreeWebMailMallocs(); + +extern int _MYTIMEZONE; + +extern HKEY REGTREE; +extern char * REGTREETEXT; + +extern HBRUSH bgBrush; +extern BOOL cfgMinToTray; + +extern CIRCUIT * Console; + +extern ULONG ChatApplMask; +extern char Verstring[]; + +extern char SignoffMsg[]; +extern char AbortedMsg[]; +extern char InfoBoxText[]; // Text to display in Config Info Popup + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +extern HWND MainWnd; +extern char BaseDir[]; +extern char BaseDirRaw[]; +extern char MailDir[]; +extern char WPDatabasePath[]; +extern char RlineVer[50]; + +extern BOOL LogBBS; +extern BOOL LogCHAT; +extern BOOL LogTCP; +extern BOOL ForwardToMe; +extern BOOL OnlyKnown; + +extern BOOL AllowAnon; +extern BOOL UserCantKillT; +extern BOOL DontNeedHomeBBS; + +extern int LatestMsg; +extern char BBSName[]; +extern char SYSOPCall[]; +extern char BBSSID[]; +extern char NewUserPrompt[]; + +extern char * WelcomeMsg; +extern char * NewWelcomeMsg; +extern char * ChatWelcomeMsg; +extern char * NewChatWelcomeMsg; +extern char * ExpertWelcomeMsg; + +extern char * Prompt; +extern char * NewPrompt; +extern char * ExpertPrompt; + +// Filter Params + +extern char ** RejFrom; // Reject on FROM Call +extern char ** RejTo; // Reject on TO Call +extern char ** RejAt; // Reject on AT Call +extern char ** RejBID; + +extern char ** HoldFrom; // Hold on FROM Call +extern char ** HoldTo; // Hold on TO Call +extern char ** HoldAt; // Hold on AT Call +extern char ** HoldBID; + +// Send WP Params + +extern BOOL SendWP; +extern char SendWPVIA[81]; +extern char SendWPTO[11]; +extern int SendWPType; + +extern int Ver[4]; + +extern struct MsgInfo ** MsgHddrPtr; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern struct NNTPRec * FirstNNTPRec; +//extern int NumberofNNTPRecs; + + +extern int NumberofMessages; +extern int FirstMessageIndextoForward; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern struct SEM AllocSemaphore; +extern struct SEM ConSemaphore; +extern struct SEM MsgNoSemaphore; + +extern struct MsgInfo * MsgnotoMsg[]; // Message Number to Message Slot List. + + +extern char hostname[]; +extern char RtUsr[]; +extern char RtUsrTemp[]; +extern char RtKnown[]; +extern int AXIPPort; +extern BOOL NeedStatus; + +extern BOOL ISP_Gateway_Enabled; +extern BOOL SMTPAuthNeeded; + + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; +extern int UserLifetime; + +extern int MaxRXSize; +extern int MaxTXSize; + +extern char OurNode[]; +extern char OurAlias[]; +extern BOOL SMTPMsgCreated; + +extern HINSTANCE hInst; +extern HWND hWnd; +extern RECT MainRect; + +extern char BBSName[]; +extern char HRoute[]; +extern char AMPRDomain[]; +extern BOOL SendAMPRDirect; +extern int BBSApplNum; +extern int SMTPInPort; +extern int POP3InPort; +extern int NNTPInPort; +extern BOOL RemoteEmail; + +extern int MaxStreams; +extern UCHAR * OtherNodes; +extern struct UserInfo * BBSChain; // Chain of users that are BBSes +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; +extern int HighestBBSNumber; +extern HMENU hFWDMenu; // Forward Menu Handle +extern char zeros[]; // For forward bitmask tests +extern BOOL EnableUI; +extern BOOL RefuseBulls; +extern BOOL SendSYStoSYSOPCall; +extern BOOL SendBBStoSYSOPCall; +extern BOOL DontHoldNewUsers; +extern BOOL DefaultNoWINLINK; +extern BOOL UIEnabled[]; +extern BOOL UINull[]; +extern BOOL UIMF[]; +extern BOOL UIHDDR[]; +extern char * UIDigi[]; +extern int MailForInterval; +extern char MailForText[]; + +extern BOOL ISP_Gateway_Enabled; + +extern char MyDomain[]; // Mail domain for BBS<>Internet Mapping + +extern char ISPSMTPName[]; +extern char ISPEHLOName[]; +extern int ISPSMTPPort; + +extern char ISPPOP3Name[]; +extern int ISPPOP3Port; +extern int ISPPOP3Interval; + +extern char ISPAccountName[]; +extern char ISPAccountPass[]; +extern char EncryptedISPAccountPass[]; +extern int EncryptedPassLen; +extern char *month[]; + +extern HWND hDebug; +extern RECT MonitorRect; +extern RECT DebugRect; +extern HWND hMonitor; +//extern HWND hConsole; +//extern RECT ConsoleRect; +extern int LogAge; +extern BOOL DeletetoRecycleBin; +extern BOOL SuppressMaintEmail; +extern BOOL SaveRegDuringMaint; +extern BOOL SendWP; +extern BOOL OverrideUnsent; +extern BOOL SendNonDeliveryMsgs; +extern BOOL GenerateTrafficReport; + +extern double PR; +extern double PUR; +extern double PF; +extern double PNF; +extern int BF; +extern int BNF; +extern int NTSD; +extern int NTSU; +extern int NTSF; +extern int AP; +extern int AB; + +extern struct Override ** LTFROM; +extern struct Override ** LTTO; +extern struct Override ** LTAT; + +extern time_t LastHouseKeepingTime; +extern time_t LastTrafficTime; + +extern char * MyElements[]; +extern char ** AliasText; +extern struct ALIAS ** Aliases; + +extern BOOL ReaddressLocal; +extern BOOL ReaddressReceived; +extern BOOL WarnNoRoute; +extern BOOL SendPtoMultiple; +extern BOOL Localtime; + +struct ConsoleInfo * ConsHeader[2]; + +extern BOOL NeedHomeBBS; +extern char ConfigName[250]; +extern BOOL UsingingRegConfig; + +extern BOOL MulticastRX; + +extern BOOL FilterWPBulls; +extern BOOL NoWPGuesses; +extern char ** SendWPAddrs; // Replacers WP To and VIA + +extern BOOL DontCheckFromCall; + +// YAPP stuff + +#define SOH 1 +#define STX 2 +#define ETX 3 +#define EOT 4 +#define ENQ 5 +#define ACK 6 +#define DLE 0x10 +#define NAK 0x15 +#define CAN 0x18 diff --git a/cMain.c b/cMain.c index 22a8fa2..1624f80 100644 --- a/cMain.c +++ b/cMain.c @@ -1050,6 +1050,30 @@ BOOL Start() PORT->SmartIDInterval = PortRec->SmartID; + if (PortRec->KissParams && (PORT->PORTTYPE == 0 || PORT->PORTTYPE == 22)) + { + struct KISSINFO * KISS = (struct KISSINFO *)PORT; + UCHAR KissString[128]; + int KissLen = 0; + unsigned char * Kissptr = KissString; + char * ptr; + char * Context; + + ptr = strtok_s(PortRec->KissParams, " ", &Context); + + while (ptr && ptr[0] && KissLen < 120) + { + *(Kissptr++) = atoi (ptr); + KissLen++; + ptr = strtok_s(NULL, " ", &Context); + } + + KISS->KISSCMD = malloc(256); + + KISS->KISSCMDLEN = KissEncode(KissString, KISS->KISSCMD, KissLen); + realloc(KISS->KISSCMD, KISS->KISSCMDLEN); + } + if (PortRec->BBSFLAG) // Appl 1 no permitted - BBSFLAG=NOBBS PORT->PERMITTEDAPPLS &= 0xfffffffe; // Clear bottom bit diff --git a/config.c b/config.c index 5a10ab3..6592b51 100644 --- a/config.c +++ b/config.c @@ -255,6 +255,7 @@ int simple(int i); int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); int doSerialPortName(int i, char * value, char * rec); int doPermittedAppls(int i, char * value, char * rec); +int doKissCommand(int i, char * value, char * rec); BOOL ProcessAPPLDef(char * rec); BOOL ToLOC(double Lat, double Lon , char * Locator); @@ -360,7 +361,7 @@ static char *pkeywords[] = "BCALL", "DIGIMASK", "NOKEEPALIVES", "COMPORT", "DRIVER", "WL2KREPORT", "UIONLY", "UDPPORT", "IPADDR", "I2CBUS", "I2CDEVICE", "UDPTXPORT", "UDPRXPORT", "NONORMALIZE", "IGNOREUNLOCKEDROUTES", "INP3ONLY", "TCPPORT", "RIGPORT", "PERMITTEDAPPLS", "HIDE", -"SMARTID"}; /* parameter keywords */ +"SMARTID", "KISSCOMMAND"}; /* parameter keywords */ static void * poffset[] = { @@ -374,7 +375,7 @@ static void * poffset[] = &xxp.BCALL, &xxp.DIGIMASK, &xxp.DefaultNoKeepAlives, &xxp.IOADDR, &xxp.DLLNAME, &xxp.WL2K, &xxp.UIONLY, &xxp.IOADDR, &xxp.IPADDR, &xxp.INTLEVEL, &xxp.IOADDR, &xxp.IOADDR, &xxp.ListenPort, &xxp.NoNormalize, &xxp.IGNOREUNLOCKED, &xxp.INP3ONLY, &xxp.TCPPORT, &xxp.RIGPORT, &xxp.PERMITTEDAPPLS, &xxp.Hide, -&xxp.SmartID}; /* offset for corresponding data in config file */ +&xxp.SmartID, &xxp.KissParams}; /* offset for corresponding data in config file */ static int proutine[] = { @@ -388,7 +389,7 @@ static int proutine[] = 0, 1, 2, 18, 15, 16, 2, 1, 17, 1, 1, 1, 1, 2, 2, 2, 1, 1, 19, 2, -1}; /* routine to process parameter */ +1, 20}; /* routine to process parameter */ int PPARAMLIM = sizeof(proutine)/sizeof(int); @@ -2209,7 +2210,11 @@ int decode_port_rec(char * rec) break; case 19: - cn = doPermittedAppls(i,value,rec); // COMPORT + cn = doPermittedAppls(i,value,rec); // Permitted Apps + break; + + case 20: + cn = doKissCommand(i, value, rec); // Permitted Apps break; @@ -2365,7 +2370,7 @@ int doPermittedAppls(int i, char * value, char * rec) char * Context; char * ptr1 = strtok_s(value, " ,=\t\n\r", &Context); - // Param is a comma separated list of Appl Numbers allowes to connect on this port + // Param is a comma separated list of Appl Numbers allowed to connect on this port while (ptr1 && ptr1[0]) { @@ -2378,6 +2383,13 @@ int doPermittedAppls(int i, char * value, char * rec) return 1; } +int doKissCommand(int i, char * value, char * rec) +{ + // Param is kiss command and any operands as decimal bytes + + xxp.KissParams = _strdup(strlop(rec, '=')); + return 1; +} int hwtypes(i, value, rec) @@ -2612,7 +2624,7 @@ static int troutine[] = #define TPARAMLIM 6 -extern CMDX COMMANDLIST[]; +extern CMDX TNCCOMMANDLIST[]; extern int NUMBEROFTNCCOMMANDS; int decode_tnc_rec(char * rec) @@ -2689,7 +2701,7 @@ int decode_tnc_rec(char * rec) // Try process as TNC2 Command int n = 0; - CMDX * CMD = &COMMANDLIST[0]; + CMDX * CMD = &TNCCOMMANDLIST[0]; char * ptr1 = key_word; UCHAR * valueptr; diff --git a/configstructs.h b/configstructs.h index 605d2e1..39fc25b 100644 --- a/configstructs.h +++ b/configstructs.h @@ -1,4 +1,7 @@ +#ifndef CONFIGSTRUCT +#define CONFIGSTRUCT + // MAKE SURE SHORTS ARE CORRECTLY ALLIGNED FOR ARMV5 @@ -73,6 +76,7 @@ struct PORTCONFIG long long txOffset; // Transverter tx offset long long rxOffset; // Transverter rx offset ppa int SmartID; + unsigned char * KissParams; }; struct ROUTECONFIG @@ -177,5 +181,7 @@ struct UPNP char * WANPort; }; +#endif + diff --git a/kiss.c b/kiss.c index 55c8f98..0de9ae9 100644 --- a/kiss.c +++ b/kiss.c @@ -34,12 +34,15 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #include #include #include +#ifndef RP2040 #include #include +#include +#endif #include #include #include -#include + //#include @@ -113,7 +116,7 @@ int ConnecttoTCP(NPASYINFO ASY); int KISSGetTCPMessage(NPASYINFO ASY); VOID CloseKISSPort(struct PORTCONTROL * PortVector); int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); -void processDRATSFrame(unsigned char * Message, int Len); +void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr); extern struct PORTCONTROL * PORTTABLE; extern int NUMBEROFPORTS; @@ -157,7 +160,7 @@ int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count) if (ret == -1) { Debugprintf ("i2c Write Error\r"); - usleep(1000); + Sleep(1); ret = i2c_smbus_write_byte(Port->idComDev, *(ptr)); } ptr++; @@ -306,11 +309,15 @@ int ASYINIT(int comport, int speed, struct PORTCONTROL * PortVector, char Channe } else if (PortVector->PORTIPADDR.s_addr || PortVector->KISSSLAVE) { + +#ifndef RP2040 + SOCKET sock; u_long param=1; BOOL bcopt=TRUE; struct sockaddr_in sinx; + // KISS over UDP or TCP if (PortVector->ListenPort == 0) @@ -423,8 +430,8 @@ int ASYINIT(int comport, int speed, struct PORTCONTROL * PortVector, char Channe npKISSINFO->RXBPTR=&npKISSINFO->RXBUFFER[0]; npKISSINFO->RXMPTR=&npKISSINFO->RXMSG[0]; - OpenConnection(PortVector, comport); - + OpenConnection(PortVector); +#endif } npKISSINFO->Portvector = PortVector; @@ -452,9 +459,10 @@ NPASYINFO CreateKISSINFO( int port,int speed ) -HANDLE OpenConnection(struct PORTCONTROL * PortVector, int port) +HANDLE OpenConnection(struct PORTCONTROL * PortVector) { NPASYINFO npKISSINFO = KISSInfo[PortVector->PORTNUMBER]; + struct KISSINFO * KISS = (struct KISSINFO *) PortVector; HANDLE ComDev = 0 ; if (npKISSINFO == NULL) @@ -474,8 +482,6 @@ HANDLE OpenConnection(struct PORTCONTROL * PortVector, int port) { // RFM22/23 module or TNC-PI- send a reset - struct KISSINFO * KISS = (struct KISSINFO *) PortVector; - ENCBUFF[0] = FEND; ENCBUFF[1] = KISS->OURCTRL | 15; // Action command ENCBUFF[2] = 2; // Reset command @@ -487,13 +493,15 @@ HANDLE OpenConnection(struct PORTCONTROL * PortVector, int port) { // SCS Tracker - Send Enter KISS (CAN)(ESC)@K(CR) - struct KISSINFO * KISS = (struct KISSINFO *) PortVector; - memcpy(ENCBUFF, "\x18\x1b@K\r", 5); // Enter KISS ASYSEND(PortVector, ENCBUFF, 5); } + if (KISS->KISSCMD && KISS->KISSCMDLEN) + ASYSEND(PortVector, KISS->KISSCMD, KISS->KISSCMDLEN); + + return ComDev; } int ReadCommBlock(NPASYINFO npKISSINFO, char * lpszBlock, int nMaxLength ) @@ -509,7 +517,7 @@ int ReadCommBlock(NPASYINFO npKISSINFO, char * lpszBlock, int nMaxLength ) if (npKISSINFO->ReopenTimer > 300) // about 30 secs { - npKISSINFO->idComDev = OpenConnection(npKISSINFO->Portvector, npKISSINFO->bPort); + npKISSINFO->idComDev = OpenConnection(npKISSINFO->Portvector); npKISSINFO->ReopenTimer = 0; } } @@ -1573,7 +1581,7 @@ SeeifMore: VEC = PORT->Session->L4TARGET.HOST; C_Q_ADD(&PORT->Session->L4TX_Q, (UINT *)Buffer); -#ifndef LINBPQ +#ifdef BPQ32 if (VEC) PostMessage(VEC->HOSTHANDLE, BPQMsg, VEC->HOSTSTREAM, 2); #endif @@ -1588,13 +1596,13 @@ SeeifMore: } // checksum if necessary - +#ifndef RP2040 if (KISS->PORT.KISSFLAGS & DRATS) { - processDRATSFrame(&Port->RXMSG[1], len - 2); + processDRATSFrame(&Port->RXMSG[1], len - 2, 0); return 0; } - +#endif if (len < 15) return 0; // too short for AX25 @@ -1821,6 +1829,7 @@ int ConnecttoTCP(NPASYINFO ASY) VOID ConnecttoTCPThread(NPASYINFO ASY) { +#ifndef RP2040 char Msg[255]; int err,i; u_long param=1; @@ -1829,6 +1838,7 @@ VOID ConnecttoTCPThread(NPASYINFO ASY) // struct hostent * HostEnt; SOCKADDR_IN sinx; int addrlen=sizeof(sinx); + struct KISSINFO * KISS = (struct KISSINFO *) ASY->Portvector; sinx.sin_family = AF_INET; sinx.sin_addr.s_addr = INADDR_ANY; @@ -1886,9 +1896,15 @@ VOID ConnecttoTCPThread(NPASYINFO ASY) ASY->Connecting = FALSE; ioctlsocket (sock, FIONBIO, ¶m); + + // If configured send TNC command + + if (KISS->KISSCMD && KISS->KISSCMDLEN) + send(sock, KISS->KISSCMD, KISS->KISSCMDLEN, 0); + continue; } - else + else { err=WSAGetLastError(); @@ -1910,10 +1926,13 @@ VOID ConnecttoTCPThread(NPASYINFO ASY) } Sleep (57000/2); // 1/2 Mins } +#endif } int KISSGetTCPMessage(NPASYINFO ASY) { +#ifndef RP2040 + int index=0; ULONG param = 1; @@ -1989,6 +2008,8 @@ int KISSGetTCPMessage(NPASYINFO ASY) { // Reopen Listening Socket + + SOCKET sock; u_long param=1; BOOL bcopt=TRUE; @@ -2020,7 +2041,7 @@ int KISSGetTCPMessage(NPASYINFO ASY) else ASY->Listening = TRUE; } - } +#endif return 0; } diff --git a/kiss.h b/kiss.h index 8a9aded..b45df2d 100644 --- a/kiss.h +++ b/kiss.h @@ -45,7 +45,7 @@ typedef struct tagASYINFO } ASYINFO, *NPASYINFO ; -NPASYINFO KISSInfo[33] = {0}; +NPASYINFO KISSInfo[MAXBPQPORTS] = {0}; #define _fmemset memset @@ -59,6 +59,6 @@ NPASYINFO CreateKISSINFO( int port, int speed ); BOOL DestroyKISSINFO(NPASYINFO npKISSINFO) ; int ReadCommBlock(NPASYINFO npKISSINFO, char * lpszBlock, int nMaxLength); static BOOL WriteCommBlock(NPASYINFO npKISSINFO, char * lpByte, DWORD dwBytesToWrite); -HANDLE OpenConnection(struct PORTCONTROL * PortVector, int port); +HANDLE OpenConnection(struct PORTCONTROL * PortVector); BOOL SetupConnection(NPASYINFO npKISSINFO); BOOL CloseConnection(NPASYINFO npKISSINFO); diff --git a/makefile b/makefile index a1cc332..184cfbf 100644 --- a/makefile +++ b/makefile @@ -23,7 +23,7 @@ all: CFLAGS = -DLINBPQ -MMD -g -fcommon all: linbpq -noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g +noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -fcommon noi2c: linbpq diff --git a/rigcontrol.h b/rigcontrol.h index badd22a..bd3c942 100644 --- a/rigcontrol.h +++ b/rigcontrol.h @@ -1,3 +1,6 @@ +#ifndef RIGCONTROL +#define RIGCONTROL + #ifndef LINBPQ #include "Rigresource.h" #endif @@ -75,7 +78,7 @@ struct RIGINFO struct _EXTPORTDATA * PortRecord[32]; // BPQ32 port record(s) for this rig (null terminated list) UCHAR RigAddr; - int ScanStopped; // Scanning enabled if zero. Bits used for interlocked scanning (eg winmor/pactor on same port + uint64_t ScanStopped; // Scanning enabled if zero. Bits used for interlocked scanning (eg winmor/pactor on same port int ScanCounter; int PollCounter; // Don't poll too often; int ScanFreq; // Scan Rate @@ -261,3 +264,5 @@ struct RIGPORTINFO #define W98_SERIAL_GETDATA 0x801 #define W98_SERIAL_SETDATA 0x802 + +#endif diff --git a/telnetserver.h b/telnetserver.h index 072f14c..77da27c 100644 --- a/telnetserver.h +++ b/telnetserver.h @@ -1,3 +1,5 @@ +#ifndef TELNETSERVER +#define TELNETSERVER #ifndef LINBPQ //#include "resource.h" @@ -80,7 +82,7 @@ struct ConnectionInfo #define NOP 241 //No operation #define xDM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. #define BRK 243 //Break Indicates that the "break" or "attention" key was hi. -#define IP 244 //Suspend Interrupt or abort the process to which the NVT is connected. +#define IPx 244 //Suspend Interrupt or abort the process to which the NVT is connected. #define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. #define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. #define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. @@ -104,3 +106,5 @@ struct ConnectionInfo #define linemode 34 //1184 #define environmentvariables 36 //1408 +#endif + diff --git a/winstdint.h b/winstdint.h index 1e52351..8c44d8e 100644 --- a/winstdint.h +++ b/winstdint.h @@ -76,8 +76,8 @@ typedef unsigned __STDINT_LONGLONG uint_least64_t; */ typedef char int_fast8_t; typedef unsigned char uint_fast8_t; -typedef short int_fast16_t; -typedef unsigned short uint_fast16_t; +//typedef short int_fast16_t; +//typedef unsigned short uint_fast16_t; typedef int int_fast32_t; typedef unsigned int uint_fast32_t; typedef __STDINT_LONGLONG int_fast64_t;