/*
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



#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__)


#include "bpqmail.h"

char * APIENTRY GetBPQDirectory();

int BBSLogAge = 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 DeleteBBSLogFiles();

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();

	DeleteBBSLogFiles();

	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 - (time_t)PR*86400;
	PURLimit = now -(time_t)PUR*86400;
	PFLimit = now - (time_t)PF*86400;
	PNFLimit = now - (time_t)PNF*86400;
	BFLimit = now - (time_t)BF*86400;
	BNFLimit = now -(time_t) 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, (int)PUR);

						KillMsg(Msg);
					}
				}
				else
				{
					if (Msg->datecreated < PNFLimit)
					{
						if (SendNonDeliveryMsgs) 
							SendNonDeliveryMessage(Msg, FALSE, (int)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 DeleteBBSLogFiles()
{
   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 > BBSLogAge)
		 {
			 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 DeleteBBSLogFiles()
{
	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 > BBSLogAge)
				{
					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);
}
