/* 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 // // Support for FLAMP compatible Mulitcast #include "bpqmail.h" void decodeblock( unsigned char in[4], unsigned char out[3]); // Base64 Decode time_t MulticastMaxAge = 48 * 60 * 60; // 48 Hours in secs struct MSESSION * MSessions = NULL; #ifndef LINBPQ #include "AFXRES.h" HWND hMCMonitor = NULL; HWND MCList; static HMENU hMCMenu; // handle of menu static char MCClassName[]="BPQMCWINDOW"; RECT MCMonitorRect; static int Height, Width, LastY; #define BGCOLOUR RGB(236,233,216) void MCMoveWindows() { RECT rcClient; int ClientWidth, ClientHeight; GetClientRect(hMCMonitor, &rcClient); if (rcClient.bottom == 0) // Minimised return; ClientHeight = rcClient.bottom; ClientWidth = rcClient.right; MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); } void CopyMCToClipboard(HWND hWnd); LRESULT CALLBACK MCWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; LPRECT lprc; struct MSESSION * Sess = MSessions; struct MSESSION * Temp; switch (message) { case WM_ACTIVATE: break; case WM_CLOSE: if (wParam) // Used by Close All Programs. return 0; return (DefWindowProc(hWnd, message, wParam, lParam)); case WM_COMMAND: wmId = LOWORD(wParam); // Remember, these are... wmEvent = HIWORD(wParam); // ...different for Win32! switch (wmId) { case ID_EDIT_COPY: CopyMCToClipboard(hMCMonitor); return 0;; case ID_EDIT_CLEAR: while (Sess) { ListView_DeleteItem(MCList, Sess->Index); if (Sess->FileName) free(Sess->FileName); if (Sess->OrigTimeStamp) free(Sess->OrigTimeStamp); if (Sess->Message) free(Sess->Message); if (Sess->BlockList) free(Sess->BlockList); if (Sess->ID) free(Sess->ID); Temp = Sess; Sess = Sess->Next; } MSessions = NULL; return 0; default: return 0; } case WM_SYSCOMMAND: wmId = LOWORD(wParam); // Remember, these are... wmEvent = HIWORD(wParam); // ...different for Win32! switch (wmId) { case SC_MINIMIZE: if (cfgMinToTray) return ShowWindow(hWnd, SW_HIDE); default: return (DefWindowProc(hWnd, message, wParam, lParam)); } case WM_SIZING: lprc = (LPRECT) lParam; Height = lprc->bottom-lprc->top; Width = lprc->right-lprc->left; MCMoveWindows(); return TRUE; case WM_DESTROY: // Remove the subclass from the edit control. GetWindowRect(hWnd, &MonitorRect); // For save soutine if (cfgMinToTray) DeleteTrayMenuItem(hWnd); hMCMonitor = NULL; break; default: return (DefWindowProc(hWnd, message, wParam, lParam)); } return (0); } static void MoveMCWindows() { RECT rcMain, rcClient; int ClientHeight, ClientWidth; GetWindowRect(hMCMonitor, &rcMain); GetClientRect(hMCMonitor, &rcClient); ClientHeight = rcClient.bottom; ClientWidth = rcClient.right; // MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); // MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); // MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); } HWND CreateMCListView (HWND hwndParent) { INITCOMMONCONTROLSEX icex; // Structure for control initialization. HWND hList; LV_COLUMN Column; LOGFONT lf; HFONT hFont; int n = 0; memset(&lf, 0, sizeof(LOGFONT)); lf.lfHeight = 12; lf.lfWidth = 8; lf.lfPitchAndFamily = FIXED_PITCH; strcpy (lf.lfFaceName, "FIXEDSYS"); hFont = CreateFontIndirect(&lf); icex.dwICC = ICC_LISTVIEW_CLASSES; InitCommonControlsEx(&icex); // Create the list-view window in report view with label editing enabled. hList = CreateWindow(WC_LISTVIEW, "Messages", WS_CHILD | LVS_REPORT | LVS_EDITLABELS, 0, 0, 100, 100, hwndParent, (HMENU)NULL, hInst, NULL); SendMessage(hList, WM_SETFONT,(WPARAM) hFont, 0); ListView_SetExtendedListViewStyle(hList,LVS_EX_FULLROWSELECT); Column.cx=45; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="ID"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); Column.cx=95; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="From"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); Column.cx=140; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="FileName"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); Column.cx=50; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="Size"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); Column.cx=40; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="%"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); Column.cx=55; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="Time"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); Column.cx=55; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="Age"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); Column.cx=20; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="C"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); Column.cx=430; Column.mask=LVCF_WIDTH | LVCF_TEXT; Column.pszText="BlockList"; SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM) &Column); ShowWindow(hList, SW_SHOWNORMAL); UpdateWindow(hList); return (hList); } #define OurSetItemText(hwndLV, i, iSubItem_, pszText_) \ { LV_ITEM _ms_lvi;\ _ms_lvi.iSubItem = iSubItem_;\ _ms_lvi.pszText = pszText_;\ SNDMSG((hwndLV), LVM_SETITEMTEXT, (WPARAM)i, (LPARAM)(LV_ITEM FAR *)&_ms_lvi);\ } void RefreshMCLine(struct MSESSION * MSession) { LV_ITEM Item; LVFINDINFO Finfo; int ret, n, pcent; char Time[80]; char Agestring[80]; char Size[16] = "??"; char Percent[16] = "??"; char Key[16]; char BlockList[101] = ""; struct tm * TM; time_t Age; if (MCList == 0) return; sprintf(Key, "%04X", MSession->Key); Age = time(NULL) - MSession->LastUpdated; // if (LocalTime) // TM = localtime(&MSession->LastUpdated); // else TM = gmtime(&Age); sprintf(Agestring, "%.2d:%.2d", TM->tm_hour, TM->tm_min); TM = gmtime(&MSession->Created); sprintf(Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); Finfo.flags = LVFI_STRING; Finfo.psz = Key; Finfo.vkDirection = VK_DOWN; ret = SendMessage(MCList, LVM_FINDITEM, (WPARAM)-1, (LPARAM) &Finfo); if (ret == -1) { n = ListView_GetItemCount(MCList); MSession->Index = n; } else MSession->Index = ret; Item.mask=LVIF_TEXT; Item.iItem = MSession->Index; Item.iSubItem = 0; Item.pszText = Key; ret = SendMessage(MCList, LVM_SETITEMTEXT, (WPARAM)MSession->Index, (LPARAM) &Item); if (ret == 0) MSession->Index = ListView_InsertItem(MCList, &Item); sprintf(Size, "%d", MSession->MessageLen); if (MSession->MessageLen) { int i; pcent = (MSession->BlocksReceived * 100) / MSession->BlockCount; sprintf(Percent, "%d", pcent); // Flag received blocks. Normalise to 50 wide memset(BlockList, '.', 50); for (i = 0; i < 50; i++) { int posn = (i * MSession->BlockCount) / 50; if (MSession->BlockList[posn] == 1) BlockList[i] = 'Y'; } } n = 0; OurSetItemText(MCList, MSession->Index, n++, Key); if (MSession->ID) OurSetItemText(MCList, MSession->Index, n++, MSession->ID) else OurSetItemText(MCList, MSession->Index, n++, " "); OurSetItemText(MCList, MSession->Index, n++, MSession->FileName); OurSetItemText(MCList, MSession->Index, n++, Size); OurSetItemText(MCList, MSession->Index, n++, Percent); OurSetItemText(MCList, MSession->Index, n++, Time); OurSetItemText(MCList, MSession->Index, n++, Agestring); if (MSession->Completed) OurSetItemText(MCList, MSession->Index, n++, "Y") else OurSetItemText(MCList, MSession->Index, n++, " "); OurSetItemText(MCList, MSession->Index, n++, BlockList); } BOOL CreateMulticastConsole() { WNDCLASS wc; HBRUSH bgBrush; RECT rcClient; if (hMCMonitor) { ShowWindow(hMCMonitor, SW_SHOWNORMAL); SetForegroundWindow(hMCMonitor); return FALSE; // Already open } bgBrush = CreateSolidBrush(BGCOLOUR); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = MCWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA; wc.hInstance = hInst; wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = bgBrush; wc.lpszMenuName = NULL; wc.lpszClassName = MCClassName; RegisterClass(&wc); hMCMonitor=CreateDialog(hInst, MCClassName, 0, NULL); if (!hMCMonitor) return (FALSE); hMCMenu=GetMenu(hMCMonitor); // CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); // CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); // CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); DrawMenuBar(hMCMonitor); // Create List View GetClientRect (hMCMonitor, &rcClient); MCList = CreateMCListView(hMCMonitor); MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); if (cfgMinToTray) AddTrayMenuItem(hMCMonitor, "Mail Multicast Monitor"); ShowWindow(hMCMonitor, SW_SHOWNORMAL); if (MCMonitorRect.right < 100 || MCMonitorRect.bottom < 100) { GetWindowRect(hMCMonitor, &MCMonitorRect); } MoveWindow(hMCMonitor, MCMonitorRect.left, MCMonitorRect.top, MCMonitorRect.right-MCMonitorRect.left, MCMonitorRect.bottom-MCMonitorRect.top, TRUE); MoveMCWindows(); return TRUE; } void CopyMCToClipboard(HWND hWnd) { int i,n, len=0; char * Buffer; HGLOBAL hMem; char * ptr; char Time[80]; char Agestring[80]; char From[16]; char Size[16]; char Percent[16]; char FileName[128]; char Key[16]; char Complete[2]; char BlockList[128]; n = ListView_GetItemCount(MCList); Buffer = malloc((n + 1) * 200); len = sprintf(Buffer, "ID From FileName Size %% Time Age Blocklist\r\n"); for (i=0; i> 1) ^ 0xA001; else crcval = (crcval >> 1); } } static unsigned int CalcCRC(UCHAR * ptr, int Len) { int i; crcval = 0xFFFF; for (i = 0; i < Len; i++) { update(*ptr++); } return crcval; } struct MSESSION * FindMSession(unsigned int Key) { struct MSESSION * Sess = MSessions; struct MSESSION * LastSess = NULL; while (Sess) { if (Sess->Key == Key) return Sess; LastSess = Sess; Sess = Sess->Next; } // Not found Sess = zalloc(sizeof(struct MSESSION)); if (Sess == NULL) return NULL; Sess->Key = Key; Sess->Created = time(NULL); if (LastSess) LastSess->Next = Sess; else MSessions = Sess; return Sess; } #include "LzmaLib.h" #define LZMA_STR "\1LZMA" UCHAR * LZUncompress(UCHAR * Decoded, int Len, int * NewLen) { unsigned char * buf; unsigned char inprops[LZMA_PROPS_SIZE]; size_t inlen; int r; UINT rlen; UINT outlen; memcpy(&rlen, &Decoded[5], 4); outlen = ntohl(rlen); *NewLen = outlen; buf = malloc(outlen); if (outlen > 1 << 25) { Debugprintf("Refusing to decompress data (> 32 MiB)"); return NULL; } memcpy(inprops, Decoded + strlen(LZMA_STR) + sizeof(int), LZMA_PROPS_SIZE); inlen = Len - strlen(LZMA_STR) - sizeof(int) - LZMA_PROPS_SIZE; if ((r = LzmaUncompress(buf, &outlen, (const unsigned char*)Decoded + Len - inlen, &inlen, inprops, LZMA_PROPS_SIZE)) != SZ_OK) { Debugprintf("Lzma Uncompress failed: %s", LZMA_ERRORS[r]); return NULL; } else { return buf; } } void decodeblock128(unsigned char in[8], unsigned char out[7] ) { out[0] = (unsigned char) (in[0] << 1 | in[1] >> 6); out[1] = (unsigned char) (in[1] << 2 | in[2] >> 5); out[2] = (unsigned char) (in[2] << 3 | in[3] >> 4); out[3] = (unsigned char) (in[3] << 4 | in[4] >> 3); out[4] = (unsigned char) in[4] << 5 | in[5] >> 2; out[5] = (unsigned char) in[5] << 6 | in[6] >> 1; out[6] = (unsigned char) in[6] << 7 | in[7]; } void SaveMulticastMessage(struct MSESSION * MSession) { UCHAR * Decoded = NULL; // Output from Basexxx decode UCHAR * Uncompressed = NULL; int DecodedLen; // Length of decoded message int UncompressedLen; // Length of decompressed message int ExpectedLen; // From front of Base128 or Base256 message int HddrLen; // Length of Expected Len Header if (MSession->FileName == NULL) return; // Need Name MSession->Completed = TRUE; // So we don't get it again // If compresses and encoded, decode and decompress if (memcmp(MSession->Message, "[b64:start]", 11) == 0) { UCHAR * ptr1 = &MSession->Message[11]; UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller int Len = MSession->MessageLen - 21; // Header and Trailer Decoded = ptr2; // Decode Base64 encoding while (Len > 0) { decodeblock(ptr1, ptr2); ptr1 += 4; ptr2 += 3; Len -= 4; } DecodedLen = (int)(ptr2 - Decoded); Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); } else if (memcmp(MSession->Message, "[b128:start]", 12) == 0) { UCHAR * ptr1 = &MSession->Message[12]; UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller UCHAR ch; UCHAR * Intermed; int Len = MSession->MessageLen - 23; // Header and Trailer Intermed = ptr2; // Decode Base128 encoding // First remove transparency (as in base256) // Extract decoded msg len ExpectedLen = atoi(ptr1); ptr1 = strchr(ptr1, 10); ptr1++; HddrLen = (int)(ptr1 - &MSession->Message[12]); if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) { Debugprintf("MCAST Missing Length Field"); return; } Len -= HddrLen;; while (Len > 0) { ch = *(ptr1++); Len --; if (ch == ':') { ch = *(ptr1++); Len--; switch (ch) { case ':' : *(ptr2++) = ':'; break; case '0' : *(ptr2++) = 0x00; break; case '1' : *(ptr2++) = 0x01; break; case '2' : *(ptr2++) = 0x02; break; case '3' : *(ptr2++) = 0x03; break; case '4' : *(ptr2++) = 0x04; break; case '5' : *(ptr2++) = 0x05; break; case '6' : *(ptr2++) = 0x06; break; case '7' : *(ptr2++) = 0x07; break; case '8' : *(ptr2++) = 0x08; break; case '9' : *(ptr2++) = 0x09; break; case 'A' : *(ptr2++) = '\n'; break; case 'B' : *(ptr2++) = '\r'; break; case 'C' : *(ptr2++) = '^'; break; case 'D' : *(ptr2++) = 0x7F; break; case 'E' : *(ptr2++) = 0xFF; break; } } else *(ptr2++) = ch; } Len = ptr2 - Intermed; ptr1 = Intermed; ptr2 = malloc(MSession->MessageLen); // Must get smaller Decoded = ptr2; while (Len > 0) { decodeblock128(ptr1, ptr2); ptr1 += 8; ptr2 += 7; Len -= 8; } DecodedLen = ptr2 - Decoded; Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); } else if (memcmp(MSession->Message, "[b256:start]", 12) == 0) { UCHAR * ptr1 = &MSession->Message[12]; UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller UCHAR ch; int Len = MSession->MessageLen - 23; // Header and Trailer Decoded = ptr2; // Decode Base256 encoding // Extract decoded msg len ExpectedLen = atoi(ptr1); ptr1 = strchr(ptr1, 10); ptr1++; HddrLen = ptr1 - &MSession->Message[12]; if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) { Debugprintf("MCAST Missing Length Field"); return; } Len -= HddrLen;; while (Len > 0) { ch = *(ptr1++); Len --; if (ch == ':') { ch = *(ptr1++); Len--; switch (ch) { case ':' : *(ptr2++) = ':'; break; case '0' : *(ptr2++) = 0x00; break; case '1' : *(ptr2++) = 0x01; break; case '2' : *(ptr2++) = 0x02; break; case '3' : *(ptr2++) = 0x03; break; case '4' : *(ptr2++) = 0x04; break; case '5' : *(ptr2++) = 0x05; break; case '6' : *(ptr2++) = 0x06; break; case '7' : *(ptr2++) = 0x07; break; case '8' : *(ptr2++) = 0x08; break; case '9' : *(ptr2++) = 0x09; break; case 'A' : *(ptr2++) = '\n'; break; case 'B' : *(ptr2++) = '\r'; break; case 'C' : *(ptr2++) = '^'; break; case 'D' : *(ptr2++) = 0x7F; break; case 'E' : *(ptr2++) = 0xFF; break; } } else *(ptr2++) = ch; } DecodedLen = ptr2 - Decoded; Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); } else { // Plain Text UncompressedLen = MSession->MessageLen; Uncompressed = MSession->Message; MSession->Message = NULL; // So we dont try to free again } if (Decoded) free(Decoded); if (Uncompressed) { // Write it away and free it char MsgFile[MAX_PATH]; FILE * hFile; int WriteLen=0; UCHAR * ptr1 = Uncompressed; // Make Sure MCAST directory exists sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST", MailDir); #ifdef WIN32 CreateDirectory(MsgFile, NULL); // Just in case #else mkdir(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); chmod(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); #endif sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST/%s", MailDir, MSession->FileName); hFile = fopen(MsgFile, "wb"); if (hFile) { WriteLen = (int)fwrite(Uncompressed, 1, UncompressedLen, hFile); fclose(hFile); } // if it looks like an export file (Starts SP SB or ST) and ends /ex // import and delete it. if (*(ptr1) == 'S' && ptr1[2] == ' ') if (_memicmp(&ptr1[UncompressedLen - 5], "/EX", 3) == 0) ImportMessages(NULL, MsgFile, TRUE); free (Uncompressed); } } VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) { char Opcode[80]; unsigned int checksum, len = 0; char * data; int headerlen = 0; unsigned int crcval; int n; unsigned int Key; struct MSESSION * MSession; if (MsgLen == 1 && Buffer[0] == 13) return; MsgLen --; // Remove the CR we added Buffer[MsgLen] = 0; if (MsgLen == 1 && Buffer[0] == 13) return; // return; n = sscanf(&Buffer[1], "%s %04d %04X", Opcode, &len, &checksum); if (n != 3) return; data = strchr(Buffer, '>'); if (data) headerlen = (int)(++data - Buffer); if (headerlen + len != MsgLen) return; crcval = CalcCRC(data, len); if (checksum != crcval) return; // Extract Session Key sscanf(&data[1], "%04X", &Key); MSession = FindMSession(Key); if (MSession == 0) return; // ?? couldn't allocate MSession->LastUpdated = time(NULL); if (MSession->Completed) return; // We already have it all if (strcmp(Opcode, "ID") == 0) { strlop(&data[6], ' '); MSession->ID = _strdup(&data[6]); return; } if (strcmp(Opcode, "PROG") == 0) { // Ignore for now return; } if (strcmp(Opcode, "FILE") == 0) { // {80BC}20141108142542:debug_log.txt char * FN = strchr(&data[6], ':'); if (FN) { *(FN++) = 0; MSession->FileName = _strdup(FN); MSession->OrigTimeStamp = _strdup(&data[6]); } // We could get whole message without getting the Name, // so check if (MSession->BlockCount && MSession->BlocksReceived == MSession->BlockCount) { // We have the whole message. Decode and Save if (MSession->MessageLen) // Also need length SaveMulticastMessage(MSession); } RefreshMCLine(MSession); return; } if (strcmp(Opcode, "SIZE") == 0) { // SIZE 14 2995>{80BC}465 8 64 int a, b, c, n = sscanf(&data[6], "%d %d %d", &a, &b, &c); if (n == 3) { // We may already have some (or even all) the message if we // missed the SIZE block first time round if (MSession->Message) { // Already have at least part of it if (MSession->BlockSize != c) { // We based blocksize on last packet, so need to sort out mess // Find where we put the block, and move it UCHAR * OldLoc; MSession->Message = realloc(MSession->Message, a); MSession->BlockList = realloc(MSession->BlockList, b); OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; memmove(&MSession->Message[(MSession->BlockCount - 1) * c], OldLoc, MSession->BlockSize); MSession->BlockSize = c; } if (MSession->BlockCount < b) { // Dont have it all, so need to extend ; MSession->Message = realloc(MSession->Message, a); MSession->BlockList = realloc(MSession->BlockList, b); } } MSession->MessageLen = a; MSession->BlockCount = b; MSession->BlockSize = c; if (MSession->Message == NULL) { MSession->Message = zalloc(b * c); MSession->BlockList = zalloc(b); } // We might have it all now if (MSession->BlocksReceived == MSession->BlockCount) { // We have the whole message. Decode and Save SaveMulticastMessage(MSession); } } RefreshMCLine(MSession); return; } if (strcmp(Opcode, "DATA") == 0) { // {80BC:1}[b256:start]401 int Blockno = atoi(&data[6]); char * dataptr = strchr(&data[6], '}'); if (dataptr == 0) return; dataptr++; // What should we do if we don't have Filename or Size?? // If we assume this isn't the last block, then we can get // the block size from this message. This is pretty save, but // I guess as we will only get one last block, if we subsequently // get an earlier one that is bigger, we can recalculate the position // of this block and move it. if (MSession->MessageLen == 0) { // Haven't received SIZE Message yet. Guess the blocksize int blocksize = MsgLen - (int)(dataptr - Buffer); if (MSession->BlockSize == 0) { MSession->BlockSize = blocksize; } else { if (MSession->BlockSize < blocksize) { // We based blocksize on last packet, so need to sort out mess // Find where we put the block, and move it UCHAR * OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; memmove(&MSession->Message[(MSession->BlockCount - 1) * blocksize], OldLoc, MSession->BlockSize); MSession->BlockSize = blocksize; } } // We need to realloc Message and Blocklist if this is a later block if (MSession->BlockCount < Blockno) { MSession->Message = realloc(MSession->Message, Blockno * MSession->BlockSize); MSession->BlockList = realloc(MSession->BlockList, Blockno); memset(&MSession->BlockList[MSession->BlockCount], 0, Blockno - MSession->BlockCount); MSession->BlockCount = Blockno; } } if (Blockno == 0 || Blockno > MSession->BlockCount) return; Blockno--; if (MSession->BlockList[Blockno] == 1) { // Already have this block return; } memcpy(&MSession->Message[Blockno * MSession->BlockSize], dataptr, MSession->BlockSize); MSession->BlockList[Blockno] = 1; MSession->BlocksReceived++; if (MSession->BlocksReceived == MSession->BlockCount && MSession->MessageLen) { // We have the whole message. Decode and Save SaveMulticastMessage(MSession); } RefreshMCLine(MSession); return; } MsgLen++; /* QST DE GM8BPQ {80BC}FLAMP 2.2.03 {80BC}20141108142542:debug_log.txt {80BC}GM8BPQ Skigersta {80BC}465 8 64 {80BC:1}[b256:start]401 :1LZMA:0:0:6-]:0:0:0:4:0$--:7m?8-v\-E-Y-[--- {80BC:2}rS-)N{j--o--ZMPX-,-l-yD------E--;-o:6-|;--f---q----0<---%-- {80BC:3}*N-?N--*:Cf{:9--z-J:9-HMd:8-------Q--D---_-a----:C$;A-j---(: {80BC:4}DWb--K---Qq-uj-_--;i------T-\>-{:6---~-ij~-,-(-O--2--+ -:8 {80BC:5}p---:7Gf:E-5o->x---4--K--:3-\:E---gouuH-3'----:A!.:7 --N:0S {80BC:6}.---/-~#.-:D:7zg~--m--:8-'---Y%-?--ze\-ho:5-}-:C:A:1u-1-O- - {80BC:7}9p-42-w--G:2G:3--g--O---n----c-#----DF-!~--:D-A--|-e------- {80BC:8}B{--:0 [b256:end] {80BC:EOF} {80BC:EOT} DE GM8BPQ K */ 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 = 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 = 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) { int 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 = &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 MCastConTimer(ConnectionInfo * conn) { conn->MCastListenTime--; if (conn->MCastListenTime == 0) Disconnect(conn->BPQStream); } VOID MCastTimer() { struct MSESSION * Sess = MSessions; struct MSESSION * Prev = NULL; time_t Now = time(NULL); while (Sess) { if (Sess->Completed == FALSE) RefreshMCLine(Sess); if (Now - Sess->LastUpdated > MulticastMaxAge) { // remove from list #ifndef LINBPQ ListView_DeleteItem(MCList, Sess->Index); #endif if (Prev) Prev->Next = Sess->Next; else MSessions = Sess->Next; if (Sess->FileName) free(Sess->FileName); if (Sess->OrigTimeStamp) free(Sess->OrigTimeStamp); if (Sess->Message) free(Sess->Message); if (Sess->BlockList) free(Sess->BlockList); if (Sess->ID) free(Sess->ID); free(Sess); return; // Saves messing with chain } Prev = Sess; Sess = Sess->Next; } } int MulticastStatusHTML(char * Reply) { char StatusPage [] = "
ID    From      FileName        Size  %%  Time   Age   Blocklist"
		"                                                   "
		"\r\n

"; int Len = 0; char Unknown[] = "???"; struct MSESSION * Sess = MSessions; if (Sess ==NULL) return 0; Len = sprintf(Reply, StatusPage); while (Sess) { char Percent[16] = "???"; char BlockList[51] = ""; int i; char Time[80]; char Agestring[80]; struct tm * TM; time_t Age; char * ID = Unknown; char * FileName = Unknown; Age = time(NULL) - Sess->LastUpdated; TM = gmtime(&Age); sprintf(Agestring, "%.2d:%.2d", TM->tm_hour, TM->tm_min); TM = gmtime(&Sess->Created); sprintf(Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); if (Sess->MessageLen && Sess->BlockCount) { int pcent; pcent = (Sess->BlocksReceived * 100) / Sess->BlockCount; sprintf(Percent, "%d", pcent); } // Flag received blocks. Normalise to 50 wide memset(BlockList, '.', 50); if (Sess->BlockList) { for (i = 0; i < 50; i++) { int posn = (i * Sess->BlockCount) / 50; if (Sess->BlockList[posn] == 1) BlockList[i] = 'Y'; } } if (Sess->FileName) FileName = Sess->FileName; if (Sess->ID) ID = Sess->ID; Len += sprintf(&Reply[Len], "%04X %-10s%-15s%5d %-3s %s %s %s\r\n", Sess->Key, ID, FileName, Sess->MessageLen, Percent, Time, Agestring, BlockList); Sess = Sess->Next; } Len += sprintf(&Reply[Len], StatusTail); return Len; }