linbpq/Housekeeping.c

1185 lines
25 KiB
C

/*
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
//
// Housekeeping Module
#include "bpqmail.h"
char * APIENTRY GetBPQDirectory();
int LogAge = 7;
BOOL DeletetoRecycleBin = FALSE;
BOOL SuppressMaintEmail = FALSE;
BOOL GenerateTrafficReport = TRUE;
BOOL SaveRegDuringMaint = FALSE;
BOOL OverrideUnsent = FALSE;
BOOL SendNonDeliveryMsgs = TRUE;
VOID UpdateWP();
double PR = 30;
double PUR = 30;
double PF = 30;
double PNF = 30;
int BF = 30;
int BNF = 30;
//int AP;
//int AB;
int NTSD = 30;
int NTSF = 30;
int NTSU = 30;
char LTFROMString[2048];
char LTTOString[2048];
char LTATString[2048];
struct Override ** LTFROM;
struct Override ** LTTO;
struct Override ** LTAT;
int DeleteLogFiles();
VOID SendNonDeliveryMessage(struct MsgInfo * OldMsg, BOOL Forwarded, int Age);
int CreateWPMessage();
int DeleteRedundantMessages();
VOID CreateUserReport();
UCHAR * APIENTRY GetLogDirectory();
time_t LastHouseKeepingTime;
time_t LastTrafficTime;
void DeletetoRecycle(char * FN)
{
#ifdef WIN32
SHFILEOPSTRUCT FileOp;
FileOp.hwnd = NULL;
FileOp.wFunc = FO_DELETE;
FileOp.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR | FOF_ALLOWUNDO;
FileOp.pFrom = FN;
FileOp.pTo = NULL;
SHFileOperation(&FileOp);
#else
// On Linux move to Deleted under current directory
char newName[256];
char oldName[256];
strcpy(oldName, FN);
char * old = FN;
mkdir("Deleted", S_IRWXU | S_IRWXG | S_IRWXO); // Make sure exists
while(strchr(old, '/'))
{
old = strlop(old, '/');
}
sprintf(newName, "Deleted/%s", old);
rename(oldName, newName);
#endif
}
VOID FreeOverride(struct Override ** Hddr)
{
struct Override ** Save;
if (Hddr)
{
Save = Hddr;
while(Hddr[0])
{
free(Hddr[0]->Call);
free(Hddr[0]);
Hddr++;
}
free(Save);
}
}
VOID FreeOverrides()
{
FreeOverride(LTFROM);
FreeOverride(LTTO);
FreeOverride(LTAT);
}
VOID * GetOverrides(config_setting_t * group, char * ValueName)
{
char * ptr1;
char * MultiString = NULL;
char * ptr;
int Count = 0;
struct Override ** Value;
char * Val;
config_setting_t *setting;
Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values
Value[0] = NULL;
setting = config_setting_get_member (group, ValueName);
if (setting)
{
ptr = (char *)config_setting_get_string (setting);
while (ptr && strlen(ptr))
{
ptr1 = strchr(ptr, '|');
if (ptr1)
*(ptr1++) = 0;
Value = realloc(Value, (Count+2) * sizeof(void *));
Value[Count] = zalloc(sizeof(struct Override));
Val = strlop(ptr, ',');
if (Val == NULL)
break;
Value[Count]->Call = _strupr(_strdup(ptr));
Value[Count++]->Days = atoi(Val);
ptr = ptr1;
}
}
Value[Count] = NULL;
return Value;
}
VOID * RegGetOverrides(HKEY hKey, char * ValueName)
{
#ifdef LINBPQ
return NULL;
#else
int retCode,Type,Vallen;
char * MultiString;
int ptr, len;
int Count = 0;
struct Override ** Value;
char * Val;
Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values
Value[0] = NULL;
Vallen=0;
retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen);
if ((retCode != 0) || (Vallen == 0))
return FALSE;
MultiString = malloc(Vallen);
retCode = RegQueryValueEx(hKey, ValueName, 0,
(ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen);
ptr=0;
while (MultiString[ptr])
{
len=strlen(&MultiString[ptr]);
Value = realloc(Value, (Count+2) * sizeof(void *));
Value[Count] = zalloc(sizeof(struct Override));
Val = strlop(&MultiString[ptr], ',');
if (Val == NULL)
break;
Value[Count]->Call = _strupr(_strdup(&MultiString[ptr]));
Value[Count++]->Days = atoi(Val);
ptr+= (len + 1);
}
Value[Count] = NULL;
free(MultiString);
return Value;
#endif
}
int Removed;
int Killed;
int BIDSRemoved;
#ifndef LINBPQ
INT_PTR CALLBACK HKDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
int Command;
switch (message)
{
case WM_INITDIALOG:
SetDlgItemInt(hDlg, IDC_REMOVED, Removed, FALSE);
SetDlgItemInt(hDlg, IDC_KILLED, Killed, FALSE);
SetDlgItemInt(hDlg, IDC_LIVE, NumberofMessages - Killed, FALSE);
SetDlgItemInt(hDlg, IDC_TOTAL, NumberofMessages, FALSE);
SetDlgItemInt(hDlg, IDC_BIDSREMOVED, BIDSRemoved, FALSE);
SetDlgItemInt(hDlg, IDC_BIDSLEFT, NumberofBIDs, FALSE);
return (INT_PTR)TRUE;
case WM_COMMAND:
Command = LOWORD(wParam);
switch (Command)
{
case IDOK:
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return 0;
}
#endif
VOID DoHouseKeeping(BOOL Manual)
{
time_t NOW;
CreateUserReport();
UpdateWP();
DeleteLogFiles();
RemoveKilledMessages();
ExpireMessages();
GetSemaphore(&AllocSemaphore, 0);
ExpireBIDs();
FreeSemaphore(&AllocSemaphore);
if (LatestMsg > MaxMsgno)
{
GetSemaphore(&MsgNoSemaphore, 0);
GetSemaphore(&AllocSemaphore, 0);
Renumber_Messages();
FreeSemaphore(&AllocSemaphore);
FreeSemaphore(&MsgNoSemaphore);
}
if (!SuppressMaintEmail)
MailHousekeepingResults();
LastHouseKeepingTime = NOW = time(NULL);
SaveMessageDatabase();
SaveConfig(ConfigName);
GetConfig(ConfigName);
GetBadWordFile(); // Reread Badwords
#ifndef LINBPQ
if (Manual)
DialogBox(hInst, MAKEINTRESOURCE(IDD_MAINTRESULTS), hWnd, HKDialogProc);
#endif
if (SendWP)
CreateWPMessage();
return;
}
VOID ExpireMessages()
{
struct MsgInfo * Msg;
int n;
time_t PRLimit;
time_t PURLimit;
time_t PFLimit;
time_t PNFLimit;
time_t BFLimit;
time_t BNFLimit;
time_t BLimit;
time_t NTSDLimit;
time_t NTSULimit;
time_t NTSFLimit;
struct Override ** Calls;
time_t now = time(NULL);
time_t Future = now + (7 * 86400);
Killed = 0;
PRLimit = now - PR*86400;
PURLimit = now - PUR*86400;
PFLimit = now - PF*86400;
PNFLimit = now - PNF*86400;
BFLimit = now - BF*86400;
BNFLimit = now - BNF*86400;
if (NTSU == 0)
{
// Assume all unset
NTSD = 30;
NTSU = 30;
NTSF = 30;
}
NTSDLimit = now - NTSD*86400;
NTSULimit = now - NTSU*86400;
NTSFLimit = now - NTSF*86400;
for (n = 1; n <= NumberofMessages; n++)
{
Msg = MsgHddrPtr[n];
// If from the future, Kill it
if (Msg->datecreated > Future)
{
KillMsg(Msg);
continue;
}
switch (Msg->type)
{
case 'P':
switch (Msg->status)
{
case 'N':
case 'H':
// Is it unforwarded or unread?
if (memcmp(Msg->fbbs, zeros, NBMASK) == 0)
{
if (Msg->datecreated < PURLimit)
{
if (SendNonDeliveryMsgs)
SendNonDeliveryMessage(Msg, TRUE, PUR);
KillMsg(Msg);
}
}
else
{
if (Msg->datecreated < PNFLimit)
{
if (SendNonDeliveryMsgs)
SendNonDeliveryMessage(Msg, FALSE, PNF);
KillMsg(Msg);
}
}
continue;
case 'F':
if (Msg->datechanged < PFLimit) KillMsg(Msg);
continue;
case 'Y':
if (Msg->datechanged < PRLimit) KillMsg(Msg);
continue;
default:
continue;
}
case 'T':
switch (Msg->status)
{
case 'F':
if (Msg->datechanged < NTSFLimit)
KillMsg(Msg);
continue;
case 'D':
if (Msg->datechanged < NTSDLimit)
KillMsg(Msg);
continue;
default:
if (Msg->datecreated < NTSULimit)
{
if (SendNonDeliveryMsgs)
SendNonDeliveryMessage(Msg, TRUE, NTSU);
KillMsg(Msg);
}
continue;
}
case 'B':
BLimit = BF;
BNFLimit = now - BNF*86400;
// Check FROM Overrides
if (LTFROM)
{
Calls = LTFROM;
while(Calls[0])
{
if (strcmp(Calls[0]->Call, Msg->from) == 0)
{
BLimit = Calls[0]->Days;
goto gotit;
}
Calls++;
}
}
// Check TO Overrides
if (LTTO)
{
Calls = LTTO;
while(Calls[0])
{
if (strcmp(Calls[0]->Call, Msg->to) == 0)
{
BLimit = Calls[0]->Days;
goto gotit;
}
Calls++;
}
}
// Check AT Overrides
if (LTAT)
{
Calls = LTAT;
while(Calls[0])
{
if (strcmp(Calls[0]->Call, Msg->via) == 0)
{
BLimit = Calls[0]->Days;
goto gotit;
}
Calls++;
}
}
gotit:
BFLimit = now - BLimit*86400;
if (OverrideUnsent)
if (BLimit != BF) // Have we an override?
BNFLimit = BFLimit;
switch (Msg->status)
{
case '$':
case 'N':
case ' ':
case 'H':
if (Msg->datecreated < BNFLimit)
KillMsg(Msg);
break;
case 'F':
case 'Y':
if (Msg->datecreated < BFLimit)
KillMsg(Msg);
break;
}
}
}
}
VOID KillMsg(struct MsgInfo * Msg)
{
FlagAsKilled(Msg, FALSE);
Killed++;
}
BOOL RemoveKilledMessages()
{
struct MsgInfo * Msg;
struct MsgInfo ** NewMsgHddrPtr;
char MsgFile[MAX_PATH];
int i, n;
Removed = 0;
GetSemaphore(&MsgNoSemaphore, 0);
GetSemaphore(&AllocSemaphore, 0);
FirstMessageIndextoForward = 0;
NewMsgHddrPtr = zalloc((NumberofMessages+1) * sizeof(void *));
NewMsgHddrPtr[0] = MsgHddrPtr[0]; // Copy Control Record
i = 0;
for (n = 1; n <= NumberofMessages; n++)
{
Msg = MsgHddrPtr[n];
if (Msg->status == 'K')
{
sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes%c", MailDir, Msg->number, 0);
if (DeletetoRecycleBin)
DeletetoRecycle(MsgFile);
else
DeleteFile(MsgFile);
MsgnotoMsg[Msg->number] = NULL;
free(Msg);
Removed++;
}
else
{
NewMsgHddrPtr[++i] = Msg;
if (memcmp(Msg->fbbs, zeros, NBMASK) != 0)
{
if (FirstMessageIndextoForward == 0)
FirstMessageIndextoForward = i;
}
}
}
NumberofMessages = i;
NewMsgHddrPtr[0]->number = i;
if (FirstMessageIndextoForward == 0)
FirstMessageIndextoForward = NumberofMessages;
free(MsgHddrPtr);
MsgHddrPtr = NewMsgHddrPtr;
FreeSemaphore(&MsgNoSemaphore);
FreeSemaphore(&AllocSemaphore);
return TRUE;
}
#define MESSAGE_NUMBER_MAX 100000
VOID Renumber_Messages()
{
int * NewNumber = (int *)0;
struct MsgInfo * Msg;
struct UserInfo * user = NULL;
char OldMsgFile[MAX_PATH];
char NewMsgFile[MAX_PATH];
int j, lastmsg, result;
int i, n, s;
s = sizeof(int)* MESSAGE_NUMBER_MAX;
NewNumber = malloc(s);
if (!NewNumber) return;
DeleteRedundantMessages(); // Make sure there aren't any old mail files, or renumber may fail
memset(NewNumber, 0, s);
for (i = 0; i < 100000; i++)
{
MsgnotoMsg[i] = NULL;
}
i = 0; // New Message Number
for (n = 1; n <= NumberofMessages; n++)
{
Msg = MsgHddrPtr[n];
NewNumber[Msg->number] = ++i; // Save so we can update users' last listed count
// New will always be >= old unless something has gone horribly wrong,
// so can rename in place without risk of losing a message
if (Msg->number < i)
{
#ifndef LINBPQ
MessageBox(MainWnd, "Invalid message number detected, quitting", "BPQMailChat", MB_OK);
#else
Debugprintf("Invalid message number detected, quitting");
#endif
SaveMessageDatabase();
if (NewNumber) free(NewNumber);
return;
}
if (Msg->number != i)
{
sprintf(OldMsgFile, "%s/m_%06d.mes", MailDir, Msg->number);
sprintf(NewMsgFile, "%s/m_%06d.mes", MailDir, i);
result = rename(OldMsgFile, NewMsgFile);
if (result)
{
char Errmsg[100];
sprintf(Errmsg, "Could not rename message no %d to %d, quitting", Msg->number, i);
#ifndef LINBPQ
MessageBox(MainWnd,Errmsg , "BPQMailChat", MB_OK);
#else
Debugprintf(Errmsg);
#endif
SaveMessageDatabase();
if (NewNumber) free(NewNumber);
return;
}
Msg->number = i;
MsgnotoMsg[i] = Msg;
}
}
for (n = 0; n <= NumberofUsers; n++)
{
user = UserRecPtr[n];
lastmsg = user->lastmsg;
if (lastmsg <= 0)
user->lastmsg = 0;
else
{
j = NewNumber[lastmsg];
if (j == 0)
{
// Last listed has gone. Find next above
while(++lastmsg < 65536)
{
if (NewNumber[lastmsg] != 0)
{
user->lastmsg = NewNumber[lastmsg];
break;
}
}
// Not found, so use latest
user->lastmsg = i;
break;
}
user->lastmsg = NewNumber[lastmsg];
}
}
MsgHddrPtr[0]->length = LatestMsg = i;
SaveMessageDatabase();
SaveUserDatabase();
if (NewNumber) free(NewNumber);
return;
}
BOOL ExpireBIDs()
{
BIDRec * BID;
BIDRec ** NewBIDRecPtr;
unsigned short now = LOWORD(time(NULL)/86400);
int i, n;
NewBIDRecPtr = zalloc((NumberofBIDs + 1) * sizeof(BIDRec));
NewBIDRecPtr[0] = BIDRecPtr[0]; // Copy Control Record
i = 0;
for (n = 1; n <= NumberofBIDs; n++)
{
BID = BIDRecPtr[n];
// Debugprintf("%d %d", BID->u.timestamp, now - BID->u.timestamp);
if ((now - BID->u.timestamp) < BidLifetime)
NewBIDRecPtr[++i] = BID;
}
BIDSRemoved = NumberofBIDs - i;
NumberofBIDs = i;
NewBIDRecPtr[0]->u.msgno = i;
free(BIDRecPtr);
BIDRecPtr = NewBIDRecPtr;
SaveBIDDatabase();
return TRUE;
}
VOID MailHousekeepingResults()
{
int Length=0;
char * MailBuffer = malloc(10000);
Length += sprintf(&MailBuffer[Length], "Killed Messages Removed %d\r\n", Removed);
Length += sprintf(&MailBuffer[Length], "Messages Killed %d\r\n", Killed);
Length += sprintf(&MailBuffer[Length], "Live Messages %d\r\n", NumberofMessages - Killed);
Length += sprintf(&MailBuffer[Length], "Total Messages %d\r\n", NumberofMessages);
Length += sprintf(&MailBuffer[Length], "BIDs Removed %d\r\n", BIDSRemoved);
Length += sprintf(&MailBuffer[Length], "BIDs Left %d\r\n", NumberofBIDs);
SendMessageToSYSOP("Housekeeping Results", MailBuffer, Length);
}
#ifdef WIN32
extern UCHAR LogDirectory[260];
int DeleteLogFiles()
{
WIN32_FIND_DATA ffd;
char szDir[MAX_PATH];
char File[MAX_PATH];
HANDLE hFind = INVALID_HANDLE_VALUE;
DWORD dwError=0;
LARGE_INTEGER ft;
time_t now = time(NULL);
int Age;
UCHAR * ptr;
// Prepare string for use with FindFile functions. First, copy the
// string to a buffer, then append '\*' to the directory name.
ptr = GetLogDirectory();
strcpy(szDir, ptr);
strcat(szDir, "/logs/Log_*.txt");
// Find the first file in the directory.
hFind = FindFirstFile(szDir, &ffd);
if (INVALID_HANDLE_VALUE == hFind)
{
return dwError;
}
// List all the files in the directory with some info about them.
do
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
OutputDebugString(ffd.cFileName);
}
else
{
ft.HighPart = ffd.ftCreationTime.dwHighDateTime;
ft.LowPart = ffd.ftCreationTime.dwLowDateTime;
ft.QuadPart -= 116444736000000000;
ft.QuadPart /= 10000000;
Age = (int)((now - ft.LowPart) / 86400);
if (Age > LogAge)
{
sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0);
if (DeletetoRecycleBin)
DeletetoRecycle(File);
else
DeleteFile(File);
}
}
}
while (FindNextFile(hFind, &ffd) != 0);
dwError = GetLastError();
FindClose(hFind);
return dwError;
}
#else
#include <dirent.h>
int Filter(const struct dirent * dir)
{
return memcmp(dir->d_name, "log", 3) == 0 && strstr(dir->d_name, ".txt");
}
int DeleteLogFiles()
{
struct dirent **namelist;
int n;
struct stat STAT;
time_t now = time(NULL);
int Age = 0, res;
char FN[256];
n = scandir("logs", &namelist, Filter, alphasort);
if (n < 0)
perror("scandir");
else
{
while(n--)
{
sprintf(FN, "logs/%s", namelist[n]->d_name);
if (stat(FN, &STAT) == 0)
{
Age = (now - STAT.st_mtime) / 86400;
if (Age > LogAge)
{
printf("Deleting %s\n", FN);
unlink(FN);
}
}
free(namelist[n]);
}
free(namelist);
}
return 0;
}
#endif
VOID SendNonDeliveryMessage(struct MsgInfo * OldMsg, BOOL Unread, int Age)
{
struct MsgInfo * Msg;
BIDRec * BIDRec;
char MailBuffer[1000];
char MsgFile[MAX_PATH];
FILE * hFile;
int WriteLen=0;
char From[100];
char * Via;
struct UserInfo * FromUser;
// Try to create a from Address. ( ? check RMS)
strcpy(From, OldMsg->from);
if (strcmp(From, "SYSTEM") == 0)
return; // Don't send non-deliverys SYSTEM messages
// Dont send NDN for NDN
if (strcmp(OldMsg->title, "Non-delivery Notification") == 0)
return;
FromUser = LookupCall(OldMsg->from);
if (FromUser)
{
if (FromUser->HomeBBS[0])
sprintf(From, "%s@%s", OldMsg->from, FromUser->HomeBBS);
else
sprintf(From, "%s@%s", OldMsg->from, BBSName);
}
else
{
WPRecP WP = LookupWP(OldMsg->from);
if (WP)
sprintf(From, "%s@%s", OldMsg->from, WP->first_homebbs);
}
Msg = AllocateMsgRecord();
GetSemaphore(&MsgNoSemaphore, 0);
Msg->number = ++LatestMsg;
MsgnotoMsg[Msg->number] = Msg;
FreeSemaphore(&MsgNoSemaphore);
strcpy(Msg->from, SYSOPCall);
Via = strlop(From, '@');
strcpy(Msg->to, From);
if (Via)
strcpy(Msg->via, Via);
if (strcmp(From, "RMS:") == 0)
{
strcpy(Msg->to, "RMS");
strcpy(Msg->via, OldMsg->emailfrom);
}
if (strcmp(From, "smtp:") == 0)
{
Msg->to[0] = 0;
strcpy(Msg->via, OldMsg->emailfrom);
}
if (Msg->to[0] == 0)
return;
strcpy(Msg->title, "Non-delivery Notification");
if (Unread)
Msg->length = sprintf(MailBuffer, "Your Message ID %s Subject %s to %s has not been read for %d days.\r\nMessage had been deleted.\r\n", OldMsg->bid, OldMsg->title, OldMsg->to, Age);
else
Msg->length = sprintf(MailBuffer, "Your Message ID %s Subject %s to %s could not be delivered in %d days.\r\nMessage had been deleted.\r\n", OldMsg->bid, OldMsg->title, OldMsg->to, Age);
Msg->type = 'P';
Msg->status = 'N';
Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName);
BIDRec = AllocateBIDRecord();
strcpy(BIDRec->BID, Msg->bid);
BIDRec->mode = Msg->type;
BIDRec->u.msgno = LOWORD(Msg->number);
BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
hFile = fopen(MsgFile, "wb");
if (hFile)
{
fwrite(MailBuffer, 1, Msg->length, hFile);
fclose(hFile);
}
MatchMessagetoBBSList(Msg, NULL);
}
VOID CreateBBSTrafficReport()
{
struct UserInfo * User;
int i, n;
char Line[200];
int len;
char File[MAX_PATH];
FILE * hFile;
time_t NOW = time(NULL);
int ConnectsIn;
int ConnectsOut;
// int MsgsReceived;
// int MsgsSent;
// int MsgsRejectedIn;
// int MsgsRejectedOut;
// int BytesForwardedIn;
// int BytesForwardedOut;
int TotMsgsReceived[4] = {0,0,0,0};
int TotMsgsSent[4] = {0,0,0,0};
int TotBytesForwardedIn[4] = {0,0,0,0};
int TotBytesForwardedOut[4] = {0,0,0,0};
char MsgsIn[80];
char MsgsOut[80];
char BytesIn[80];
char BytesOut[80];
char RejIn[80];
char RejOut[80];
struct tm tm;
struct tm last;
memcpy(&tm, gmtime(&NOW), sizeof(tm));
memcpy(&last, gmtime((const time_t *)&LastTrafficTime), sizeof(tm));
sprintf(File, "%s/Traffic_%02d%02d%02d.txt", BaseDir, tm.tm_year-100, tm.tm_mon+1, tm.tm_mday);
hFile = fopen(File, "wb");
if (hFile == NULL)
{
Debugprintf("Failed to create traffic.txt");
return;
}
len = sprintf(Line, " Traffic Report for %s From: %04d/%02d/%02d %02d:%02dz To: %04d/%02d/%02d %02d:%02dz\r\n",
BBSName, last.tm_year+1900, last.tm_mon+1, last.tm_mday, last.tm_hour, last.tm_min,
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min);
fwrite(Line, 1, len, hFile);
len = sprintf(Line, " Call Connects Connects Messages Messages Bytes Bytes Rejected Rejected\r\n");
fwrite(Line, 1, len, hFile);
len = sprintf(Line, " In Out Rxed(P/B/T) Sent Rxed Sent In Out\r\n\r\n");
fwrite(Line, 1, len, hFile);
for (i=1; i <= NumberofUsers; i++)
{
User = UserRecPtr[i];
ConnectsIn = User->Total.ConnectsIn - User->Last.ConnectsIn;
ConnectsOut = User->Total.ConnectsOut - User->Last.ConnectsOut;
/*
MsgsReceived = MsgsSent = MsgsRejectedIn = MsgsRejectedOut = BytesForwardedIn = BytesForwardedOut = 0;
for (n = 0; n < 4; n++)
{
MsgsReceived += User->Total.MsgsReceived[n] - User->Last.MsgsReceived[n];
MsgsSent += User->Total.MsgsSent[n] - User->Last.MsgsSent[n];
BytesForwardedIn += User->Total.BytesForwardedIn[n] - User->Last.BytesForwardedIn[n];
BytesForwardedOut += User->Total.BytesForwardedOut[n] - User->Last.BytesForwardedOut[n];
MsgsRejectedIn += User->Total.MsgsRejectedIn[n] - User->Last.MsgsRejectedIn[n];
MsgsRejectedOut += User->Total.MsgsRejectedOut[n] - User->Last.MsgsRejectedOut[n];
}
len = sprintf(Line, "%s %-7s %5d %8d %10d %10d %10d %10d %10d %10d\r\n",
(User->flags & F_BBS)? "(B)": " ",
User->Call, ConnectsIn,
ConnectsOut,
MsgsReceived,
MsgsSent,
BytesForwardedIn,
BytesForwardedOut,
MsgsRejectedIn,
MsgsRejectedOut);
*/
for (n = 0; n < 4; n++)
{
TotMsgsReceived[n] += User->Total.MsgsReceived[n] - User->Last.MsgsReceived[n];
TotMsgsSent[n] += User->Total.MsgsSent[n] - User->Last.MsgsSent[n];
TotBytesForwardedIn[n] += User->Total.BytesForwardedIn[n] - User->Last.BytesForwardedIn[n];
TotBytesForwardedOut[n] += User->Total.BytesForwardedOut[n] - User->Last.BytesForwardedOut[n];
}
sprintf(MsgsIn,"%d/%d/%d", User->Total.MsgsReceived[1] - User->Last.MsgsReceived[1],
User->Total.MsgsReceived[2] - User->Last.MsgsReceived[2],
User->Total.MsgsReceived[3] - User->Last.MsgsReceived[3]);
sprintf(MsgsOut,"%d/%d/%d", User->Total.MsgsSent[1] - User->Last.MsgsSent[1],
User->Total.MsgsSent[2] - User->Last.MsgsSent[2],
User->Total.MsgsSent[3] - User->Last.MsgsSent[3]);
sprintf(BytesIn,"%d/%d/%d", User->Total.BytesForwardedIn[1] - User->Last.BytesForwardedIn[1],
User->Total.BytesForwardedIn[2] - User->Last.BytesForwardedIn[2],
User->Total.BytesForwardedIn[3] - User->Last.BytesForwardedIn[3]);
sprintf(BytesOut,"%d/%d/%d", User->Total.BytesForwardedOut[1] - User->Last.BytesForwardedOut[1],
User->Total.BytesForwardedOut[2] - User->Last.BytesForwardedOut[2],
User->Total.BytesForwardedOut[3] - User->Last.BytesForwardedOut[3]);
sprintf(RejIn,"%d/%d/%d", User->Total.MsgsRejectedIn[1] - User->Last.MsgsRejectedIn[1],
User->Total.MsgsRejectedIn[2] - User->Last.MsgsRejectedIn[2],
User->Total.MsgsRejectedIn[3] - User->Last.MsgsRejectedIn[3]);
sprintf(RejOut,"%d/%d/%d", User->Total.MsgsRejectedOut[1] - User->Last.MsgsRejectedOut[1],
User->Total.MsgsRejectedOut[2] - User->Last.MsgsRejectedOut[2],
User->Total.MsgsRejectedOut[3] - User->Last.MsgsRejectedOut[3]);
len = sprintf(Line, "%s %-7s %5d %8d%16s%16s%16s%16s%16s%16s\r\n",
(User->flags & F_BBS)? "(B)": " ",
User->Call, ConnectsIn,
ConnectsOut,
MsgsIn,
MsgsOut,
BytesIn,
BytesOut,
RejIn,
RejOut);
fwrite(Line, 1, len, hFile);
User->Last.ConnectsIn = User->Total.ConnectsIn;
User->Last.ConnectsOut = User->Total.ConnectsOut;
for (n = 0; n < 4; n++)
{
User->Last.MsgsReceived[n] = User->Total.MsgsReceived[n];
User->Last.MsgsSent[n] = User->Total.MsgsSent[n];
User->Last.BytesForwardedIn[n] = User->Total.BytesForwardedIn[n];
User->Last.BytesForwardedOut[n] = User->Total.BytesForwardedOut[n];
User->Last.MsgsRejectedIn[n] = User->Total.MsgsRejectedIn[n];
User->Last.MsgsRejectedOut[n] = User->Total.MsgsRejectedOut[n];
}
}
sprintf(MsgsIn,"%d/%d/%d", TotMsgsReceived[1], TotMsgsReceived[2], TotMsgsReceived[3]);
sprintf(MsgsOut,"%d/%d/%d", TotMsgsSent[1], TotMsgsSent[2], TotMsgsSent[3]);
sprintf(BytesIn,"%d/%d/%d", TotBytesForwardedIn[1], TotBytesForwardedIn[2], TotBytesForwardedIn[3]);
sprintf(BytesOut,"%d/%d/%d", TotBytesForwardedOut[1], TotBytesForwardedOut[2], TotBytesForwardedOut[3]);
len = sprintf(Line, "\r\n Totals %s Messages In %s Messages Out %s"
" Bytes In %s Bytes Out\r\n", MsgsIn, MsgsOut, BytesIn, BytesOut);
fwrite(Line, 1, len, hFile);
SaveConfig(ConfigName);
GetConfig(ConfigName);
SaveUserDatabase();
fclose(hFile);
}