// Chat Server for BPQ32 Packet Switch // // // Based on MailChat Version 1.4.48.1 // Add Configurable SYSOP Call // Sept 2013 // If user tries to reconnect to same node, disconnect old session // Changed to match changed user rec format in kernel // Fix initialisation of Signoff Message // Jan 2014 // Add UTF-8 support // ???? // Get command from node, and allow topic to be selected on command line // Validate HTML Pages // Oct 2014 1.0.5.1 // Fix occasional crash in terminal part line processing // October 2015 1.0.6.1 // Recompiled for compatibility with WebMail // Feb 2016 1.0.7.1 // Send Chat Map INFO on Startup // Add Welcome Message to Web Config // July 2017 1.0.8.1 // Fix Keepalives in Linux version // November 2018 1.0.9.1 // Recompiled for Web Interface changes in Node // January 2019 1.0.10.1 // Move Config from Registry to File BPQChatServer 1.cfg // January 2019 1.0.10.2 // Fix errors in chat config file names and locations // December 2020 1.0.11.1 // Send INFO to Map every hour // Remove CMD_TO_APPL flag from Applflags // October 2021 1.0.11.2 // Recompiled for Web Interface changes in Node // February 2022 1.0.12.3 Renumbered to 6.0.23.1 to match Node // Add option for user defined help file // Add Connect Scripts // Improve connect timeouts and add link validation polls // Version 6.0.24.1 August 23 // Restore CMD_TO_APPL flag to Applflags (13) // Check for and remove names set to *RTL // Add option to run user program when chat user connects (27) // Add History (28) // Add connect scripts to config page text (31) // Fix History (31) // Stop buffer overflow in History // Allow /History to be shortened to /Hi (45) // Fix extra r charater in Chat Config Web Page //.25 // Increase size of status display buffers (7) #include "BPQChat.h" #include "Dbghelp.h" #define CHAT #include "Versions.h" #define LIBCONFIG_STATIC #include "libconfig.h" #include "GetVersion.h" #define MAX_LOADSTRING 100 BOOL WINE = FALSE; UINT BPQMsg; 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 int LastVer[4] = {0, 0, 0, 0}; // In case we need to do somthing the first time a version is run 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 extern ChatCIRCUIT ChatConnections[MaxSockets+1]; struct SEM ChatSemaphore = {0, 0}; 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 BOOL cfgMinToTray; BOOL DisconnectOnClose=FALSE; char PasswordMsg[100]="Password:"; char cfgHOSTPROMPT[100]; char cfgCTEXT[100]; char cfgLOCALECHO[100]; char AttemptsMsg[] = "Too many attempts - Disconnected\r\r"; char disMsg[] = "Disconnected by SYSOP\r\r"; char LoginMsg[]="user:"; char BlankCall[]=" "; char ChatSYSOPCall[50]; ULONG BBSApplMask; ULONG ChatApplMask; int BBSApplNum=0; int ChatApplNum=0; extern int NumberofChatStreams; //int StartStream=0; extern int MaxChatStreams; char ChatSID[]="[BPQChatServer-%d.%d.%d.%d]\r"; extern char ChatWelcomeMsg[1000]; char NewUserPrompt[100]="Please enter your Name\r>\r"; char ChatSignoffMsg[100]; char AbortedMsg[100]="\rOutput aborted\r"; char BaseDir[MAX_PATH]; char RlineVer[50]; BOOL KISSOnly = FALSE; UCHAR * OtherNodes=NULL; char OtherNodesList[1000]; char BPQDirectory[260]; extern char Position[81]; extern char PopupText[260]; extern int PopupMode; int Bells, FlashOnBell, StripLF, WarnWrap, WrapInput, FlashOnConnect, CloseWindowOnBye; char Version[32]; char ConsoleSize[32]; char MonitorSize[32]; char DebugSize[32]; 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: 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 ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK ChatColourDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); VOID SaveWindowConfig(); void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); char * lookupuser(char * call); BOOL GetChatConfig(char * ConfigName); BOOL SaveChatConfigFile(char * ConfigName); BOOL GetConfigFromRegistry(); VOID SaveStringValue(config_setting_t * group, char * name, char * value); VOID SaveIntValue(config_setting_t * group, char * name, int value); VOID SaveChatConfig(HWND hDlg); BOOL CreateChatPipeThread(); VOID WriteMiniDump(); struct _EXCEPTION_POINTERS exinfox; CONTEXT ContextRecord; EXCEPTION_RECORD ExceptionRecord; DWORD Stack[16]; BOOL Restarting = FALSE; typedef int (WINAPI FAR *FARPROCX)(); FARPROCX pRunEventProgram; int 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("BPQChat *** 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_DEBUGx, NULL, '!', "*** Error **** C Run Time Invalid Parameter Handler Called"); if (expression && function && file) { Logprintf(LOG_DEBUGx, NULL, '!', "Expression = %S", expression); Logprintf(LOG_DEBUGx, NULL, '!', "Function %S", function); Logprintf(LOG_DEBUGx, NULL, '!', "File %S Line %d", file, line); } } char ChatConfigName[MAX_PATH]; char Session[20] = "Server 1"; BOOL UsingingRegConfig = FALSE; // 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) { char Command[30]; Restarting = TRUE; Logprintf(LOG_DEBUGx, 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); if (hDebug) DeleteTrayMenuItem(hDebug); } 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); wsprintf(Command, "WAIT %s", Session); CreateProcess(ProgName, Command, 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 } HKEY OpenReg() { char Key[100]; HKEY hKey = 0; int disp; sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\Chat\\%s", Session); RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); return hKey; } int regGetIntValue(char * Key, int Default) { HKEY hKey = OpenReg(); int Type, Vallen = 4; int Val = Default; RegQueryValueEx(hKey ,Key, 0, &Type, (UCHAR *)&Val, (ULONG *)&Vallen); RegCloseKey(hKey); return Val; } BOOL regGetStringValue(char * Key, char * Value, int Len) { HKEY hKey = OpenReg(); int Type, Vallen = Len; int ret; ret = RegQueryValueEx(hKey ,Key, 0, &Type, (UCHAR *)Value, (ULONG *)&Vallen); if (ret != ERROR_SUCCESS) return FALSE; RegCloseKey(hKey); return TRUE; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; int BPQStream, n; struct _EXCEPTION_POINTERS exinfo; _invalid_parameter_handler oldHandler, newHandler; char Msg[100]; int i = 60; char * ptr; int nCodePage = GetACP(); if (strstr(lpCmdLine, "WAIT")) // If AutoRestart then Delay 60 Secs { hWnd = CreateWindow("STATIC", "Chat Restarting after Failure - Please Wait", 0, CW_USEDEFAULT, 100, 550, 70, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nCmdShow); while (i-- > 0) { wsprintf(Msg, "Chat Restarting after Failure - Please Wait %d secs.", i); SetWindowText(hWnd, Msg); Sleep(1000); } DestroyWindow(hWnd); } ptr = strstr(_strupr(lpCmdLine), "SERVER"); if (ptr) strcpy(Session, ptr); _strlwr(&Session[1]); strcpy(BPQDirectory, GetBPQDirectory()); __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_CHAT, NULL, '!', "Program Starting"); } 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 < NumberofChatStreams; n++) { BPQStream=ChatConnections[n].BPQStream; if (BPQStream) { SetAppl(BPQStream, 0, 0); Disconnect(BPQStream); DeallocateStream(BPQStream); } } CloseConsole(-2); ClearChatLinkStatus(); SendChatLinkStatus(); hWnd = CreateWindow("STATIC", "Chat 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 SendChatLinkStatus(); // Send again to reduce chance of being missed Sleep(2000); DestroyWindow(hWnd); if (ConsHeader[0]->hConsole) DestroyWindow(ConsHeader[0]->hConsole); if (ConsHeader[1]->hConsole) DestroyWindow(ConsHeader[1]->hConsole); if (hMonitor) DestroyWindow(hMonitor); if (hDebug) DestroyWindow(hDebug); if (cfgMinToTray) { DeleteTrayMenuItem(MainWnd); if (ConsHeader[0]->hConsole) DeleteTrayMenuItem(ConsHeader[0]->hConsole); if (ConsHeader[1]->hConsole) DeleteTrayMenuItem(ConsHeader[1]->hConsole); if (hMonitor) DeleteTrayMenuItem(hMonitor); if (hDebug) DeleteTrayMenuItem(hDebug); } FreeChatMemory(); n = 0; _CrtDumpMemoryLeaks(); } My__except_Routine("Close Processing"); SaveWindowConfig(); CloseBPQ32(); // Close Ext Drivers if last bpq32 process SaveChatConfigFile(ChatConfigName); 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; 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; struct stat STAT; REGTREE = GetRegistryKey(); REGTREETEXT = GetRegistryKeyText(); // 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"); } if (Session[7] == '1') sprintf(ChatConfigName, "%s/chatconfig.cfg", GetBPQDirectory()); else sprintf(ChatConfigName, "%s/chatconfig%s.cfg", GetBPQDirectory(), Session); // Set up file and directory names strcpy(BaseDir, GetBPQDirectory()); strcpy(RtUsr, BaseDir); strcat(RtUsr, "/ChatUsers.txt"); strcpy(RtUsrTemp, BaseDir); strcat(RtUsrTemp, "/ChatUsers.tmp"); strcpy(RtKnown, BaseDir); strcat(RtKnown, "/RTKnown.txt"); // Debugprintf("Users %s", RtUsr); UsingingRegConfig = FALSE; // if config file exists use it else try to get from Registry if (stat(ChatConfigName, &STAT) == -1) { UsingingRegConfig = TRUE; if (GetConfigFromRegistry()) { SaveChatConfigFile(ChatConfigName); } else { int retCode; char msg[80]; sprintf(msg, "No configuration found - Dummy Config created"); retCode = MessageBox(NULL, msg, "BPQChat", MB_OKCANCEL); if (retCode == IDCANCEL) return FALSE; SaveChatConfigFile(ChatConfigName); } } GetChatConfig(ChatConfigName); // Get Window Sizes sscanf(WindowSize,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); sscanf(DebugSize, "%d,%d,%d,%d", &DebugRect.left, &DebugRect.right, &DebugRect.top, &DebugRect.bottom); sscanf(MonitorSize, "%d,%d,%d,%d", &MonitorRect.left, &MonitorRect.right, &MonitorRect.top, &MonitorRect.bottom); sscanf(ConsoleSize, "%d,%d,%d,%d", &ConsoleRect.left, &ConsoleRect.right, &ConsoleRect.top, &ConsoleRect.bottom); hInst = hInstance; hWnd=CreateDialog(hInst,szWindowClass,0,NULL); if (!hWnd) { return FALSE; } MainWnd=hWnd; if (MainRect.right < 100 || MainRect.bottom < 100) { GetWindowRect(hWnd, &MainRect); } MoveWindow(hWnd,MainRect.left,MainRect.top, MainRect.right-MainRect.left, MainRect.bottom-MainRect.top, TRUE); GetVersionInfo(NULL); wsprintf(Title,"G8BPQ Chat Server Version %s %s", VersionString, Session); 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 fou updating menu items hTopMenu=GetMenu(MainWnd); hActionMenu=GetSubMenu(hTopMenu,0); // hFWDMenu=GetSubMenu(hActionMenu,0); hMenu=GetSubMenu(hActionMenu,0); hDisMenu=GetSubMenu(hActionMenu,1); 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, i; ChatCIRCUIT * conn; struct _EXCEPTION_POINTERS exinfo; if (message == BPQMsg) { 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_F3: CreateConsole(-2); return 0; case VK_F4: CreateMonitor(); return 0; case VK_F5: CreateDebugWindow(); return 0; case VK_TAB: return TRUE; break; } return 0; case WM_TIMER: if (wParam == 1) // Slow = 10 secs { __try { RefreshMainWindow(); CheckTimer(); ChatTimer(); } My__except_Routine("Slow Timer"); } else __try { TrytoSend(); } 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)hDisMenu) { // Set up Disconnect Menu ChatCIRCUIT * conn; char MenuLine[30]; int n; for (n = 0; n <= NumberofChatStreams-1; n++) { conn=&ChatConnections[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=&ChatConnections[wmId-IDM_DISCONNECT]; if (conn->Active) { Disconnect(conn->BPQStream); } } switch (wmId) { case IDM_LOGCHAT: ToggleParam(hMenu, hWnd, &LogCHAT, IDM_LOGCHAT); break; case IDM_CHATCONSOLE: CreateConsole(-2); break; case IDM_MONITOR: CreateMonitor(); break; case IDM_DEBUG: CreateDebugWindow(); 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/BPQ%20Mail%20and%20Chat%20Server.htm", "", NULL, SW_SHOWNORMAL); break; case IDM_CONFIG: i = DialogBox(hInst, MAKEINTRESOURCE(CHAT_CONFIG), hWnd, ConfigWndProc); i = WSAGetLastError(); break; case IDM_EDITCHATCOLOURS: DialogBox(hInst, MAKEINTRESOURCE(IDD_CHATCOLCONFIG), hWnd, ChatColourDialogProc); break; case ID_ACTIONS_UPDATECHATMAPINFO: DialogBox(hInst, MAKEINTRESOURCE(IDD_UPDATECHATMAP), hWnd, ChatMapDialogProc); 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; 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[1]->hConsole) GetWindowRect(ConsHeader[1]->hConsole, &ConsoleRect); // For save soutine if (hMonitor) GetWindowRect(hMonitor, &MonitorRect); // For save soutine if (hDebug) GetWindowRect(hDebug, &DebugRect); // For save soutine KillTimer(hWnd,1); KillTimer(hWnd,2); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } INT_PTR CALLBACK ChatMapDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { char Msg[500]; int len; switch (message) { case WM_INITDIALOG: SetDlgItemText(hDlg, IDC_MAPPOSITION, Position); SetDlgItemText(hDlg, IDC_POPUPTEXT, PopupText); SendDlgItemMessage(hDlg, IDC_CLICK, BM_SETCHECK, PopupMode , PopupMode); SendDlgItemMessage(hDlg, IDC_HOVER, BM_SETCHECK, !PopupMode , !PopupMode); return TRUE; case WM_COMMAND: switch LOWORD(wParam) { case IDSENDTOMAP: GetDlgItemText(hDlg, IDC_MAPPOSITION, Position, 80); GetDlgItemText(hDlg, IDC_POPUPTEXT, PopupText, 250); PopupMode = SendDlgItemMessage(hDlg, IDC_CLICK, BM_GETCHECK, 0 ,0); // if (AXIPPort) { len = wsprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode); if (len < 256) Send_MON_Datagram(Msg, len); } break; case IDOK: GetDlgItemText(hDlg, IDC_MAPPOSITION, Position, 80); GetDlgItemText(hDlg, IDC_POPUPTEXT, PopupText, 250); PopupMode = SendDlgItemMessage(hDlg, IDC_CLICK, BM_GETCHECK, 0 ,0); case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); return TRUE; case IDC_MAPHELP: ShellExecute(hDlg,"open", "http://www.cantab.net/users/john.wiseman/Documents/BPQChatMap.htm", "", NULL, SW_SHOWNORMAL); return TRUE; } } return FALSE; } // 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; } int RefreshMainWindow() { char msg[80]; ChatCIRCUIT * conn; int i,n, SYSOPMsgs = 0, HeldMsgs = 0, SMTPMsgs = 0; time_t now; struct tm * tm; char tim[20]; SendDlgItemMessage(MainWnd,100,LB_RESETCONTENT,0,0); for (n = 0; n < NumberofChatStreams; n++) { conn=&ChatConnections[n]; if (!conn->Active) { strcpy(msg,"Idle"); } else { if (conn->Flags & CHATLINK) { if (conn->u.link->supportsPolls) i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d %4d", "Chat Link", conn->u.link->alias, conn->BPQStream, "", conn->OutputQueueLength - conn->OutputGetPointer, conn->u.link->RTT); else i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d", "Chat Link", conn->u.link->alias, conn->BPQStream, "", conn->OutputQueueLength - conn->OutputGetPointer); } else if ((conn->Flags & CHATMODE) && conn->topic) { i=sprintf_s(msg, sizeof(msg), "%-10s %-10s %2d %-10s%5d", conn->UserPointer->Name, conn->u.user->call, conn->BPQStream, conn->topic->topic->name, conn->OutputQueueLength - conn->OutputGetPointer); } 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, "CHAT", conn->OutputQueueLength - conn->OutputGetPointer); } } } SendDlgItemMessage(MainWnd,100,LB_ADDSTRING,0,(LPARAM)msg); } SetDlgItemInt(hWnd, IDC_MSGS, NumberofMessages, FALSE); n = 0; SetDlgItemInt(hWnd, IDC_SYSOPMSGS, SYSOPMsgs, FALSE); SetDlgItemInt(hWnd, IDC_HELD, HeldMsgs, FALSE); SetDlgItemInt(hWnd, IDC_SMTP, SMTPMsgs, FALSE); SetDlgItemInt(hWnd, IDC_CHATSEM, ChatSemaphore.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 static SOCKADDR_IN local_sin; /* Local socket - internet style */ static PSOCKADDR_IN psin; SOCKET sock; BOOL GetConfigFromRegistry() { ChatApplNum = regGetIntValue("ApplNum", 0); MaxChatStreams = regGetIntValue("MaxStreams", 0); regGetStringValue("MapPosition", Position, 80); regGetStringValue("MapPopup", PopupText, 250); PopupMode = regGetIntValue("PopupMode", 0); regGetStringValue("ConsoleSize", ConsoleSize, 32); regGetStringValue("MonitorSize", MonitorSize, 32); regGetStringValue("DebugSize", DebugSize, 32); regGetStringValue("WindowSize", WindowSize, 32); regGetStringValue("Version", Version, 32 ); Bells = regGetIntValue("Bells", 0); FlashOnBell = regGetIntValue("FlashOnBell", 0); StripLF = regGetIntValue("StripLF", 0); WarnWrap = regGetIntValue("WarnWrap", 0); WrapInput = regGetIntValue("WrapInput", 0); FlashOnConnect = regGetIntValue("FlashOnConnect", 0); CloseWindowOnBye = regGetIntValue("CloseWindowOnBye", 0); return regGetStringValue("OtherChatNodes", OtherNodesList, 999); // Return false if not there } BOOL Initialise() { int i; ChatCIRCUIT * conn; HMODULE ExtDriver = LoadLibrary("bpq32.dll"); // Register message for posting by BPQDLL CheckTimer(); // Make sure init is complete BPQMsg = RegisterWindowMessage(BPQWinMsg); Retry: if (ChatApplNum) { char * ptr1 = GetApplCall(ChatApplNum); char * Save; if (*ptr1 > 0x20) { char * Context; memcpy(OurNode, ptr1, 10); strlop(OurNode, ' '); strcpy(ChatSYSOPCall, OurNode); strlop(ChatSYSOPCall, '-'); ptr1=GetApplAlias(ChatApplNum); memcpy(OurAlias, ptr1,10); strlop(OurAlias, ' '); ChatApplMask = 1<<(ChatApplNum-1); // Set up other nodes list. rtlink messes with the string so pass copy // New format can have connect scrips and separates lines with crlf, not space if (strchr(OtherNodesList, '\r')) // Has connect script entries { Save = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context); while (ptr1 && ptr1[0]) { rtlink(ptr1); ptr1 = strtok_s(NULL, "\r\n", &Context); } } else { Save = ptr1 = strtok_s(_strdup(OtherNodesList), " ,\r", &Context); while (ptr1) { rtlink(ptr1); ptr1 = strtok_s(NULL, " ,\r", &Context); } } free(Save); SetupChat(); } } else { DialogBox(hInst, MAKEINTRESOURCE(CHAT_CONFIG), hWnd, ConfigWndProc); goto Retry; } // Allocate Streams for (i=0; i < MaxChatStreams; i++) { conn = &ChatConnections[i]; conn->BPQStream = FindFreeStream(); if (conn->BPQStream == 255) break; NumberofChatStreams++; BPQSetHandle(conn->BPQStream, hWnd); SetAppl(conn->BPQStream, 3, ChatApplMask); Disconnect(conn->BPQStream); } if (cfgMinToTray) { char Text[80]; wsprintf(Text, "Chat Server %s ", Session); AddTrayMenuItem(MainWnd, Text); } SetTimer(hWnd,1,10000,NULL); // Slow Timer (10 Secs) SetTimer(hWnd,2,100,NULL); // Send to Node (100 ms) CheckMenuItem(hMenu,IDM_LOGCHAT, (LogCHAT) ? MF_CHECKED : MF_UNCHECKED); sprintf(ChatSignoffMsg, "73 de %s\r", ChatSYSOPCall); if (ChatWelcomeMsg[0] == 0) strcpy(ChatWelcomeMsg, "G8BPQ Chat Server.$WType /h for command summary.$WBringing up links to other nodes.$W" "This may take a minute or two.$WThe /p command shows what nodes are linked.$W"); RefreshMainWindow(); DeleteLogFiles(); CreateChatPipeThread(); if (ExtDriver) pRunEventProgram = GetProcAddress(ExtDriver,"_RunEventProgram@8"); return TRUE; } int CriticalErrorHandler(char * error) { Debugprintf("Critical Error %s", error); ProgramErrors = 25; CheckProgramErrors(); // Force close return 0; } VOID SaveWindowConfig() { wsprintf(MonitorSize,"%d,%d,%d,%d",MonitorRect.left,MonitorRect.right,MonitorRect.top,MonitorRect.bottom); wsprintf(DebugSize,"%d,%d,%d,%d",DebugRect.left,DebugRect.right,DebugRect.top,DebugRect.bottom); wsprintf(WindowSize,"%d,%d,%d,%d",MainRect.left,MainRect.right,MainRect.top,MainRect.bottom); wsprintf(Version,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); if (ConsoleRect.right) wsprintf(ConsoleSize,"%d,%d,%d,%d",ConsoleRect.left, ConsoleRect.right, ConsoleRect.top, ConsoleRect.bottom); return; } char InfoBoxText[100]; INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { int Command; UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: SetDlgItemText(hDlg, 5050, InfoBoxText); return (INT_PTR)TRUE; case WM_COMMAND: Command = LOWORD(wParam); switch (Command) { case 0: case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; } int GetMultiLineDialog(HWND hDialog, int DLGItem) { char Text[10000]; GetDlgItemText(hDialog, DLGItem, Text, 10000); // We can now have connect scripts for each node, so leave cr/lf if (Text[strlen(Text)-1] != '\n') // no terminating crlf? strcat(Text, "\r\n"); strcpy(OtherNodesList, Text); return TRUE; } INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { int Command; char Text[10000] = ""; char * ptr1, *ptr2, *Context; switch (message) { case WM_INITDIALOG: if (strchr(OtherNodesList, 13)) // New format with connect scripts? { strcpy(Text, OtherNodesList); /* ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r", &Context); while (ptr1 && ptr1[0]) { strcat(Text, ptr1); strcat(Text, "\r\n"); ptr1 = strtok_s(NULL, "\r", &Context); if (*(ptr1) == 10) ptr1++; } */ } else { ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), " ,\r", &Context); while (ptr1) { strcat(Text, ptr1); strcat(Text, "\r\n"); ptr1 = strtok_s(NULL, " ,\r", &Context); } free(ptr2); } SetDlgItemInt(hDlg, ID_CHATAPPL, ChatApplNum, FALSE); SetDlgItemInt(hDlg, ID_STREAMS, MaxChatStreams, FALSE); SetDlgItemText(hDlg, ID_CHATNODES, Text); // Replace $W in Welcome Message with cr lf ptr2 = ptr1 = _strdup(ChatWelcomeMsg); scan: ptr1 = strstr(ptr1, "$W"); if (ptr1) { *(ptr1++)=13; // put in cr *(ptr1++)=10; // put in lf goto scan; } SetDlgItemText(hDlg, IDM_CHATUSERMSG, ptr2); free(ptr2); return (INT_PTR)TRUE; 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_COMMAND: Command = LOWORD(wParam); if (Command == 2002) return TRUE; switch (Command) { case IDOK: case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; case SAVEWELCOME: GetDlgItemText(hDlg, IDM_CHATUSERMSG, ChatWelcomeMsg, 999); // Replace cr lf in string with $W ptr1 = ChatWelcomeMsg; scan2: ptr1 = strstr(ptr1, "\r\n"); if (ptr1) { *(ptr1++)='$'; // put in cr *(ptr1++)='W'; // put in lf goto scan2; } SaveChatConfigFile(ChatConfigName); EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; case SAVENODES: SaveChatConfig(hDlg); EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }