2022-08-28 09:35:46 +01:00
|
|
|
/*
|
|
|
|
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<n; i++)
|
|
|
|
{
|
|
|
|
// Get Items
|
|
|
|
|
|
|
|
ListView_GetItemText(MCList, i, 0, Key, 8);
|
|
|
|
ListView_GetItemText(MCList, i, 1, From, 15);
|
|
|
|
ListView_GetItemText(MCList, i, 2, FileName, 128);
|
|
|
|
ListView_GetItemText(MCList, i, 3, Size, 16);
|
|
|
|
ListView_GetItemText(MCList, i, 4, Percent, 16);
|
|
|
|
ListView_GetItemText(MCList, i, 5, Time, 80);
|
|
|
|
ListView_GetItemText(MCList, i, 6, Agestring, 80);
|
|
|
|
ListView_GetItemText(MCList, i, 7, Complete, 2);
|
|
|
|
ListView_GetItemText(MCList, i, 8, BlockList, 100);
|
|
|
|
|
|
|
|
// Add line to buffer
|
|
|
|
|
|
|
|
len += sprintf(&Buffer[len], "%4s %-10s %-16s %5s%4s %-6s%-6s%-2s%50s\r\n",
|
|
|
|
Key, From, FileName, Size, Percent, Time, Agestring, Complete, BlockList);
|
|
|
|
}
|
|
|
|
|
|
|
|
hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len+1);
|
|
|
|
|
|
|
|
if (hMem != 0)
|
|
|
|
{
|
|
|
|
ptr = GlobalLock(hMem);
|
|
|
|
|
|
|
|
if (OpenClipboard(MainWnd))
|
|
|
|
{
|
|
|
|
strcpy(ptr, Buffer);
|
|
|
|
GlobalUnlock(hMem);
|
|
|
|
EmptyClipboard();
|
|
|
|
SetClipboardData(CF_TEXT,hMem);
|
|
|
|
CloseClipboard();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
GlobalFree(hMem);
|
|
|
|
}
|
|
|
|
free(Buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
// LinBPQ
|
|
|
|
|
|
|
|
void RefreshMCLine(struct MSESSION * MSession)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static unsigned int crcval = 0xFFFF;
|
|
|
|
|
|
|
|
static void update(char c)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
crcval ^= c & 255;
|
|
|
|
for (i = 0; i < 8; ++i)
|
|
|
|
{
|
|
|
|
if (crcval & 1)
|
|
|
|
crcval = (crcval >> 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"
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
UCHAR * LZUncompress(UCHAR * Decoded, size_t Len, size_t * NewLen)
|
2022-08-28 09:35:46 +01:00
|
|
|
{
|
|
|
|
unsigned char * buf;
|
|
|
|
unsigned char inprops[LZMA_PROPS_SIZE];
|
|
|
|
size_t inlen;
|
|
|
|
int r;
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
size_t rlen = 0;
|
|
|
|
size_t outlen;
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
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;
|
2024-12-16 17:54:16 +00:00
|
|
|
size_t DecodedLen; // Length of decoded message
|
|
|
|
size_t UncompressedLen; // Length of decompressed message
|
2022-08-28 09:35:46 +01:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
// <FILE 34 2A1A>{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)
|
|
|
|
{
|
|
|
|
// <DATA 72 B21B>{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
|
|
|
|
|
|
|
|
<PROG 18 A519>{80BC}FLAMP 2.2.03
|
|
|
|
<FILE 34 2A1A>{80BC}20141108142542:debug_log.txt
|
|
|
|
<ID 22 9BF3>{80BC}GM8BPQ Skigersta
|
|
|
|
<SIZE 14 2995>{80BC}465 8 64
|
|
|
|
<DATA 72 B21B>{80BC:1}[b256:start]401
|
|
|
|
:1LZMA:0:0:6-]:0:0:0:4:0$--:7<DC3>m?8-<EM><DC1>v\-E<DLE>-Y-[---
|
|
|
|
<DATA 72 AE86>{80BC:2}rS-)N{j--o--ZMPX-<DLE>,-l-yD----<EM><EM>--E--;-o:6-|;<US>--f---q----0<---%<RS>--
|
|
|
|
<DATA 72 A08F>{80BC:3}*N-?N--<US>*:Cf{<FS>:9--z-J<CAN>:9-HMd:8-------Q--D---_-a----:C<EM>$;A-j---(:
|
|
|
|
<DATA 72 538A>{80BC:4}D<DC1>Wb<SI>--K<DC1>---Qq-uj-_<SUB>--<RS><SO>;i------T-\>-{:6---~-ij~-,-(-O--2--+
|
|
|
|
<SI>-:8
|
|
|
|
<DATA 72 35E6>{80BC:5}p---:7G<DC1><DC2>f:E<GS>-5o->x---4<GS>--K--:3-\:E---gouu<DC1>H<SYN>-3'----:A!.:7
|
|
|
|
--N:0S
|
|
|
|
<DATA 72 CA70>{80BC:6}.---/-~#<SUB>.-:D:7zg~--m--:8-'---Y<US>%-?--ze<DC1>\-ho:5-}-:C<ETB>:A:1u-1-O-<SI>
|
|
|
|
-
|
|
|
|
<DATA 72 8C88>{80BC:7}<NAK>9p-<US>42-w--<ETB>G:2G:3--g--O---n-<NAK><RS>---c-#----DF-!~--:D-A--|-e-------
|
|
|
|
<DATA 25 E6D6>{80BC:8}B{--:0
|
|
|
|
[b256:end]
|
|
|
|
<CNTL 10 7451>{80BC:EOF}
|
|
|
|
<CNTL 10 D45D>{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 [] =
|
|
|
|
"<pre>ID From FileName Size %% Time Age Blocklist"
|
|
|
|
" "
|
|
|
|
"\r\n<textarea cols=110 rows=6 name=MC>";
|
|
|
|
|
|
|
|
char StatusTail [] = "</textarea><br><br>";
|
|
|
|
int Len = 0;
|
|
|
|
char Unknown[] = "???";
|
|
|
|
|
|
|
|
struct MSESSION * Sess = MSessions;
|
|
|
|
|
|
|
|
if (Sess ==NULL)
|
|
|
|
return 0;
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
Len = sprintf(Reply, "%s", StatusPage);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
Len += sprintf(&Reply[Len], "%s", StatusTail);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
return Len;
|
|
|
|
}
|