Windows2003-3790/inetcore/outlookexpress/mailnews/store/cleanup.cpp
2020-09-30 16:53:55 +02:00

957 lines
27 KiB
C++

//--------------------------------------------------------------------------
// Cleanup.cpp
//--------------------------------------------------------------------------
#include "pch.hxx"
#include "cleanup.h"
#include "goptions.h"
#include "shlwapi.h"
#include "storutil.h"
#include "xpcomm.h"
#include "shared.h"
#include "syncop.h"
#include "storsync.h"
#include "instance.h"
#include "demand.h"
// --------------------------------------------------------------------------------
// Strings
// --------------------------------------------------------------------------------
static const LPSTR g_szCleanupWndProc = "OEStoreCleanupThread";
static const LPSTR c_szRegLastStoreCleaned = "Last Store Cleaned";
static const LPSTR c_szRegLastFolderCleaned = "Last Folder Cleaned";
// --------------------------------------------------------------------------------
// STOREFILETYPE
// --------------------------------------------------------------------------------
typedef enum tagSTOREFILETYPE {
STORE_FILE_HEADERS,
STORE_FILE_FOLDERS,
STORE_FILE_POP3UIDL,
STORE_FILE_OFFLINE,
STORE_FILE_LAST
} STOREFILETYPE;
// --------------------------------------------------------------------------------
// Globals
// --------------------------------------------------------------------------------
static BOOL g_fShutdown=FALSE;
// --------------------------------------------------------------------------------
// CCompactProgress
// --------------------------------------------------------------------------------
class CCompactProgress : public IDatabaseProgress
{
public:
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv) { return TraceResult(E_NOTIMPL); }
STDMETHODIMP_(ULONG) AddRef(void) { return(2); }
STDMETHODIMP_(ULONG) Release(void) { return(1); }
STDMETHODIMP Update(DWORD cCount) { return(g_fShutdown ? hrUserCancel : S_OK); }
};
// --------------------------------------------------------------------------------
// Globals
// --------------------------------------------------------------------------------
static DWORD g_dwCleanupThreadId=0;
static HANDLE g_hCleanupThread=NULL;
static HWND g_hwndStoreCleanup=NULL;
static UINT_PTR g_uDelayTimerId=0;
static HROWSET g_hCleanupRowset=NULL;
static BOOL g_fWorking=FALSE;
static STOREFILETYPE g_tyCurrentFile=STORE_FILE_LAST;
static ILogFile *g_pCleanLog=NULL;
static CCompactProgress g_cProgress;
// --------------------------------------------------------------------------------
// Timer Constants
// --------------------------------------------------------------------------------
#define IDT_START_CYCLE (WM_USER + 1)
#define IDT_CLEANUP_FOLDER (WM_USER + 2)
#define CYCLE_INTERVAL (1000 * 60 * 30)
// --------------------------------------------------------------------------------
// CLEANUPTRHEADCREATE
// --------------------------------------------------------------------------------
typedef struct tagCLEANUPTRHEADCREATE {
HRESULT hrResult;
HANDLE hEvent;
} CLEANUPTRHEADCREATE, *LPCLEANUPTRHEADCREATE;
// --------------------------------------------------------------------------------
// Prototypes
// --------------------------------------------------------------------------------
DWORD StoreCleanupThreadEntry(LPDWORD pdwParam);
LRESULT CALLBACK StoreCleanupWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
HRESULT CleanupStoreInitializeCycle(HWND hwnd);
HRESULT CleanupCurrentFolder(HWND hwnd);
HRESULT SetNextCleanupFolder(HWND hwnd);
HRESULT StartCleanupCycle(HWND hwnd);
HRESULT CleanupNewsgroup(LPCSTR pszFile, IDatabase *pDB, LPDWORD pcRemovedRead, LPDWORD pcRemovedExpired);
HRESULT CleanupJunkMail(LPCSTR pszFile, IDatabase *pDB, LPDWORD pcJunkDeleted);
//--------------------------------------------------------------------------
// RegisterWindowClass
//--------------------------------------------------------------------------
HRESULT RegisterWindowClass(LPCSTR pszClass, WNDPROC pfnWndProc)
{
// Locals
HRESULT hr=S_OK;
WNDCLASS WindowClass;
// Tracing
TraceCall("RegisterWindowClass");
// Register the Window Class
if (0 != GetClassInfo(g_hInst, pszClass, &WindowClass))
goto exit;
// Zero the object
ZeroMemory(&WindowClass, sizeof(WNDCLASS));
// Initialize the Window Class
WindowClass.lpfnWndProc = pfnWndProc;
WindowClass.hInstance = g_hInst;
WindowClass.lpszClassName = pszClass;
// Register the Class
if (0 == RegisterClass(&WindowClass))
{
hr = TraceResult(E_FAIL);
goto exit;
}
exit:
// Done
return hr;
}
//--------------------------------------------------------------------------
// CreateNotifyWindow
//--------------------------------------------------------------------------
HRESULT CreateNotifyWindow(LPCSTR pszClass, LPVOID pvParam, HWND *phwndNotify)
{
// Locals
HRESULT hr=S_OK;
HWND hwnd;
// Tracing
TraceCall("CreateNotifyWindow");
// Invalid ARg
Assert(pszClass && phwndNotify);
// Initialize
*phwndNotify = NULL;
// Create the Window
hwnd = CreateWindowEx(WS_EX_TOPMOST, pszClass, pszClass, WS_POPUP, 0, 0, 0, 0, NULL, NULL, g_hInst, (LPVOID)pvParam);
// Failure
if (NULL == hwnd)
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Set Return
*phwndNotify = hwnd;
exit:
// Done
return hr;
}
// --------------------------------------------------------------------------------
// DelayedStartStoreCleanup
// --------------------------------------------------------------------------------
void CALLBACK DelayedStartStoreCleanup(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
// Trace
TraceCall("DelayedStartStoreCleanup");
// Must have a timer
Assert(g_uDelayTimerId);
// Kill the Timer
KillTimer(NULL, g_uDelayTimerId);
// Set g_uDelayTimerId
g_uDelayTimerId = 0;
// Call this function with zero delay...
StartBackgroundStoreCleanup(0);
}
// --------------------------------------------------------------------------------
// StartBackgroundStoreCleanup
// --------------------------------------------------------------------------------
HRESULT StartBackgroundStoreCleanup(DWORD dwDelaySeconds)
{
// Locals
HRESULT hr=S_OK;
CLEANUPTRHEADCREATE Create={0};
// Trace
TraceCall("StartBackgroundStoreCleanup");
// Already Running ?
if (NULL != g_hCleanupThread)
return(S_OK);
// If dwDelaySeconds is NOT zero, then lets start this function later
if (0 != dwDelaySeconds)
{
// Trace
TraceInfo(_MSG("Delayed start store cleanup in %d seconds.", dwDelaySeconds));
// Set a timer to call this the delay function a little bit later
g_uDelayTimerId = SetTimer(NULL, 0, (dwDelaySeconds * 1000), DelayedStartStoreCleanup);
// Failure
if (0 == g_uDelayTimerId)
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Done
return(S_OK);
}
// Trace
TraceInfo("Starting store cleanup.");
// Initialize
Create.hrResult = S_OK;
// Create an Event to synchonize creation
Create.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (NULL == Create.hEvent)
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
// Create the inetmail thread
g_hCleanupThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StoreCleanupThreadEntry, &Create, 0, &g_dwCleanupThreadId);
if (NULL == g_hCleanupThread)
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
// Wait for StoreCleanupThreadEntry to signal the event
WaitForSingleObject(Create.hEvent, INFINITE);
// Failure
if (FAILED(Create.hrResult))
{
// Close
SafeCloseHandle(g_hCleanupThread);
// Reset Globals
g_hCleanupThread = NULL;
g_dwCleanupThreadId = 0;
// Return
hr = TrapError(Create.hrResult);
// Done
goto exit;
}
exit:
// Cleanup
SafeCloseHandle(Create.hEvent);
// Done
return(hr);
}
// ------------------------------------------------------------------------------------
// CloseBackgroundStoreCleanup
// ------------------------------------------------------------------------------------
HRESULT CloseBackgroundStoreCleanup(void)
{
// Trace
TraceCall("CloseBackgroundStoreCleanup");
// Trace
TraceInfo("Terminating Store Cleanup thread.");
// Kill the Timer
if (g_uDelayTimerId)
{
KillTimer(NULL, g_uDelayTimerId);
g_uDelayTimerId = 0;
}
// Invalid Arg
if (0 != g_dwCleanupThreadId)
{
// Assert
Assert(g_hCleanupThread && FALSE == g_fShutdown);
// Set Shutdown bit
g_fShutdown = TRUE;
// Post quit message
PostThreadMessage(g_dwCleanupThreadId, WM_QUIT, 0, 0);
// Wait for event to become signaled
WaitForSingleObject(g_hCleanupThread, INFINITE);
}
// Validate
Assert(NULL == g_hwndStoreCleanup && 0 == g_dwCleanupThreadId);
// If we have a handle
if (NULL != g_hCleanupThread)
{
// Close the thread handle
CloseHandle(g_hCleanupThread);
// Reset Globals
g_hCleanupThread = NULL;
}
// Done
return(S_OK);
}
// --------------------------------------------------------------------------------
// InitializeCleanupLogFile
// --------------------------------------------------------------------------------
HRESULT InitializeCleanupLogFile(void)
{
// Locals
HRESULT hr=S_OK;
CHAR szLogFile[MAX_PATH];
CHAR szStoreRoot[MAX_PATH];
// Trace
TraceCall("InitializeCleanupLogFile");
// Better not have a log file yet
Assert(NULL == g_pCleanLog);
// Open Log File
IF_FAILEXIT(hr = GetStoreRootDirectory(szStoreRoot, ARRAYSIZE(szStoreRoot)));
// MakeFilePath to cleanup.log
IF_FAILEXIT(hr = MakeFilePath(szStoreRoot, "cleanup.log", c_szEmpty, szLogFile, ARRAYSIZE(szLogFile)));
// Open the LogFile
IF_FAILEXIT(hr = CreateLogFile(g_hLocRes, szLogFile, "CLEANUP", 65536, &g_pCleanLog,
FILE_SHARE_READ | FILE_SHARE_WRITE));
// Write the Store root
g_pCleanLog->WriteLog(LOGFILE_DB, szStoreRoot);
exit:
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// StoreCleanupThreadEntry
// --------------------------------------------------------------------------------
DWORD StoreCleanupThreadEntry(LPDWORD pdwParam)
{
// Locals
HRESULT hr=S_OK;
MSG msg;
DWORD dw;
DWORD cb;
LPCLEANUPTRHEADCREATE pCreate;
// Trace
TraceCall("StoreCleanupThreadEntry");
// We better have a parameter
Assert(pdwParam);
// Cast to create info
pCreate = (LPCLEANUPTRHEADCREATE)pdwParam;
// Initialize OLE
hr = CoInitialize(NULL);
if (FAILED(hr))
{
TraceResult(hr);
pCreate->hrResult = hr;
SetEvent(pCreate->hEvent);
return(1);
}
// Reset Shutdown Bit
g_fShutdown = FALSE;
// OpenCleanupLogFile
InitializeCleanupLogFile();
// Registery the window class
IF_FAILEXIT(hr = RegisterWindowClass(g_szCleanupWndProc, StoreCleanupWindowProc));
// Create the notification window
IF_FAILEXIT(hr = CreateNotifyWindow(g_szCleanupWndProc, NULL, &g_hwndStoreCleanup));
// Success
pCreate->hrResult = S_OK;
// Run at a low-priority
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
// Set Event
SetEvent(pCreate->hEvent);
// Pump Messages
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Kill the timer
if (g_uDelayTimerId)
{
KillTimer(NULL, g_uDelayTimerId);
g_uDelayTimerId = 0;
}
// Kill the Current Timer
KillTimer(g_hwndStoreCleanup, IDT_CLEANUP_FOLDER);
// Kill the timer
KillTimer(g_hwndStoreCleanup, IDT_START_CYCLE);
// Kill the Window
DestroyWindow(g_hwndStoreCleanup);
g_hwndStoreCleanup = NULL;
g_dwCleanupThreadId = 0;
// Release LogFile
SafeRelease(g_pCleanLog);
exit:
// Failure
if (FAILED(hr))
{
pCreate->hrResult = hr;
SetEvent(pCreate->hEvent);
}
// Uninit
CoUninitialize();
// Done
return 1;
}
// --------------------------------------------------------------------------------
// StoreCleanupWindowProc
// --------------------------------------------------------------------------------
LRESULT CALLBACK StoreCleanupWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Trace
TraceCall("StoreCleanupWindowProc");
// Switch
switch(msg)
{
// OnCreate
case WM_CREATE:
// Set the time to start the first cycle in one second
SetTimer(hwnd, IDT_START_CYCLE, 1000, NULL);
// Done
return(0);
// OnTime
case WM_TIMER:
// Cleanup Folder Timer
if (IDT_CLEANUP_FOLDER == wParam)
{
// Kill the Current Timer
KillTimer(hwnd, IDT_CLEANUP_FOLDER);
// Cleanup the Next Folder
CleanupCurrentFolder(hwnd);
}
// Start new cleanup cycle
else if (IDT_START_CYCLE == wParam)
{
// Kill the timer
KillTimer(hwnd, IDT_START_CYCLE);
// Start a new cycle
StartCleanupCycle(hwnd);
}
// Done
return(0);
// OnDestroy
case WM_DESTROY:
// Close the Current Rowset
g_pLocalStore->CloseRowset(&g_hCleanupRowset);
// Done
return(0);
}
// Deletegate
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// --------------------------------------------------------------------------------
// StartCleanupCycle
// --------------------------------------------------------------------------------
HRESULT StartCleanupCycle(HWND hwnd)
{
// Locals
HRESULT hr=S_OK;
// Trace
TraceCall("StartCleanupCycle");
// Validate State
Assert(g_pLocalStore && NULL == g_hCleanupRowset);
// Logfile
if (g_pCleanLog)
{
// WriteLogFile
g_pCleanLog->WriteLog(LOGFILE_DB, "Starting Background Cleanup Cycle...");
}
// Create a Store Rowset
IF_FAILEXIT(hr = g_pLocalStore->CreateRowset(IINDEX_SUBSCRIBED, NOFLAGS, &g_hCleanupRowset));
// Set state
g_tyCurrentFile = STORE_FILE_HEADERS;
// Cleanup this folder...
SetTimer(hwnd, IDT_CLEANUP_FOLDER, 100, NULL);
exit:
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// CleanupCurrentFolder
// --------------------------------------------------------------------------------
HRESULT CleanupCurrentFolder(HWND hwnd)
{
// Locals
HRESULT hr=S_OK;
LPCSTR pszFile=NULL;
FOLDERTYPE tyFolder=FOLDER_INVALID;
SPECIALFOLDER tySpecial=FOLDER_NOTSPECIAL;
FOLDERINFO Folder={0};
DWORD cRecords;
DWORD cbAllocated;
DWORD cbFreed;
DWORD cbStreams;
DWORD cbFile;
DWORD dwWasted;
DWORD dwCompactAt;
DWORD cRemovedRead=0;
DWORD cRemovedExpired=0;
DWORD cJunkDeleted=0;
IDatabase *pDB=NULL;
IMessageFolder *pFolderObject=NULL;
// Trace
TraceCall("CleanupCurrentFolder");
// Validate
Assert(g_pLocalStore);
// Get Next Folder
if (STORE_FILE_HEADERS == g_tyCurrentFile)
{
// Better have a rowset
Assert(g_hCleanupRowset);
// Get a Folder
hr = g_pLocalStore->QueryRowset(g_hCleanupRowset, 1, (LPVOID *)&Folder, NULL);
if (FAILED(hr) || S_FALSE == hr)
{
// Don with Current Cycle
g_pLocalStore->CloseRowset(&g_hCleanupRowset);
// Set g_tyCurrentFile
g_tyCurrentFile = STORE_FILE_FOLDERS;
}
// Otherwise...
else if (FOLDERID_ROOT == Folder.idFolder || ISFLAGSET(Folder.dwFlags, FOLDER_SERVER))
{
// Goto Next
goto exit;
}
// Otherwise
else
{
// Set some stuff
pszFile = Folder.pszFile;
tyFolder = Folder.tyFolder;
tySpecial = Folder.tySpecial;
// If no folder file, then jump to exit
if (NULL == pszFile)
goto exit;
// Open the folder object
if (FAILED(g_pLocalStore->OpenFolder(Folder.idFolder, NULL, OPEN_FOLDER_NOCREATE, &pFolderObject)))
goto exit;
// Get the Database
pFolderObject->GetDatabase(&pDB);
}
}
// If something other than a folder
if (STORE_FILE_HEADERS != g_tyCurrentFile)
{
// Locals
LPCTABLESCHEMA pSchema=NULL;
LPCSTR pszName=NULL;
CHAR szRootDir[MAX_PATH + MAX_PATH];
CHAR szFilePath[MAX_PATH + MAX_PATH];
// Folders
if (STORE_FILE_FOLDERS == g_tyCurrentFile)
{
pszName = pszFile = c_szFoldersFile;
pSchema = &g_FolderTableSchema;
g_tyCurrentFile = STORE_FILE_POP3UIDL;
}
// Pop3uidl
else if (STORE_FILE_POP3UIDL == g_tyCurrentFile)
{
pszName = pszFile = c_szPop3UidlFile;
pSchema = &g_UidlTableSchema;
g_tyCurrentFile = STORE_FILE_OFFLINE;
}
// Offline.dbx
else if (STORE_FILE_OFFLINE == g_tyCurrentFile)
{
pszName = pszFile = c_szOfflineFile;
pSchema = &g_SyncOpTableSchema;
g_tyCurrentFile = STORE_FILE_LAST;
}
// Otherwise, we are done
else if (STORE_FILE_LAST == g_tyCurrentFile)
{
// Set time to start next cycle
SetTimer(hwnd, IDT_START_CYCLE, CYCLE_INTERVAL, NULL);
// Done
return(S_OK);
}
// Validate
Assert(pSchema && pszName);
// No File
if (FIsEmptyA(pszFile))
goto exit;
// Get Root Directory
IF_FAILEXIT(hr = GetStoreRootDirectory(szRootDir, ARRAYSIZE(szRootDir)));
// Make File Path
IF_FAILEXIT(hr = MakeFilePath(szRootDir, pszFile, c_szEmpty, szFilePath, ARRAYSIZE(szFilePath)));
// If the File Exists?
if (FALSE == PathFileExists(szFilePath))
goto exit;
// Open a Database Object on the file
IF_FAILEXIT(hr = g_pDBSession->OpenDatabase(szFilePath, OPEN_DATABASE_NORESET, pSchema, NULL, &pDB));
}
// Not Working
g_fWorking = TRUE;
// Get Record Count
IF_FAILEXIT(hr = pDB->GetRecordCount(IINDEX_PRIMARY, &cRecords));
// If this is a news folder, and I'm the only person with it open...
if (FOLDER_NEWS == tyFolder)
{
// Cleanup Newgroup
CleanupNewsgroup(pszFile, pDB, &cRemovedRead, &cRemovedExpired);
}
// If this is the junk mail folder
if ((FOLDER_LOCAL == tyFolder) && (FOLDER_JUNK == tySpecial))
{
// Cleanup Junk Mail folder
CleanupJunkMail(pszFile, pDB, &cJunkDeleted);
}
// Get Size Information...
IF_FAILEXIT(hr = pDB->GetSize(&cbFile, &cbAllocated, &cbFreed, &cbStreams));
// Wasted
dwWasted = cbAllocated > 0 ? ((cbFreed * 100) / cbAllocated) : 0;
// Get Option about when to compact
dwCompactAt = DwGetOption(OPT_CACHECOMPACTPER);
// Trace
if (g_pCleanLog)
{
// Write
g_pCleanLog->WriteLog(LOGFILE_DB, _MSG("%12s, CompactAt: %02d%%, Wasted: %02d%%, File: %09d, Records: %08d, Allocated: %09d, Freed: %08d, Streams: %08d, RemovedRead: %d, RemovedExpired: %d, JunkDeleted: %d", pszFile, dwCompactAt, dwWasted, cbFile, cRecords, cbAllocated, cbFreed, cbStreams, cRemovedRead, cRemovedExpired, cJunkDeleted));
}
// If less than 25% wasted space and there is more than a meg allocated
if (dwWasted < dwCompactAt)
goto exit;
// Compact
hr = pDB->Compact((IDatabaseProgress *)&g_cProgress, COMPACT_PREEMPTABLE | COMPACT_YIELD);
// Log Result
if (g_pCleanLog && S_OK != hr)
{
// Write
g_pCleanLog->WriteLog(LOGFILE_DB, _MSG("IDatabase::Compact(%s) Returned: 0x%08X", pszFile, hr));
}
exit:
// Cleanup
SafeRelease(pDB);
SafeRelease(pFolderObject);
g_pLocalStore->FreeRecord(&Folder);
// Not Working
g_fWorking = FALSE;
// ShutDown
if (FALSE == g_fShutdown)
{
// Compute Next CleanupFolder
SetTimer(hwnd, IDT_CLEANUP_FOLDER, 100, NULL);
}
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// CleanupNewsgroup
// --------------------------------------------------------------------------------
HRESULT CleanupNewsgroup(LPCSTR pszFile, IDatabase *pDB, LPDWORD pcRemovedRead,
LPDWORD pcRemovedExpired)
{
// Locals
HRESULT hr=S_OK;
DWORD cExpireDays;
BOOL fRemoveExpired=FALSE;
BOOL fRemoveRead=FALSE;
DWORD cClients;
FILETIME ftCurrent;
HROWSET hRowset=NULL;
MESSAGEINFO MsgInfo={0};
// Trace
TraceCall("CleanupNewsgroup");
// Get Current Time
GetSystemTimeAsFileTime(&ftCurrent);
// Get the Number of Days in which to expire messages
cExpireDays = DwGetOption(OPT_CACHEDELETEMSGS);
// If the option is not off, set the flag
fRemoveExpired = (OPTION_OFF == cExpireDays) ? FALSE : TRUE;
// Remove Read ?
fRemoveRead = (FALSE != DwGetOption(OPT_CACHEREAD) ? TRUE : FALSE);
// Nothing to do
if (FALSE == fRemoveExpired && FALSE == fRemoveRead)
goto exit;
// Create a Rowset
IF_FAILEXIT(hr = pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
// Loop
while (S_OK == pDB->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL))
{
// If I'm not the only client, then abort the cleanup
IF_FAILEXIT(hr = pDB->GetClientCount(&cClients));
// Better be 1
if (cClients != 1)
{
hr = DB_E_COMPACT_PREEMPTED;
goto exit;
}
// Abort
if (S_OK != g_cProgress.Update(1))
{
hr = STORE_E_OPERATION_CANCELED;
goto exit;
}
// Only if this message has a body
if (!ISFLAGSET(MsgInfo.dwFlags, ARF_KEEPBODY) && !ISFLAGSET(MsgInfo.dwFlags, ARF_WATCH) && ISFLAGSET(MsgInfo.dwFlags, ARF_HASBODY) && MsgInfo.faStream)
{
// Otherwise, if expiring...
if (TRUE == fRemoveExpired && (UlDateDiff(&MsgInfo.ftDownloaded, &ftCurrent) / SECONDS_INA_DAY) >= cExpireDays)
{
// Delete this message
IF_FAILEXIT(hr = pDB->DeleteRecord(&MsgInfo));
// Count Removed Expired
(*pcRemovedExpired)++;
}
// Removing Read and this message is read ?
else if (TRUE == fRemoveRead && ISFLAGSET(MsgInfo.dwFlags, ARF_READ))
{
// Delete the Stream
pDB->DeleteStream(MsgInfo.faStream);
// No More Stream
MsgInfo.faStream = 0;
// Fixup the Record
FLAGCLEAR(MsgInfo.dwFlags, ARF_HASBODY | ARF_ARTICLE_EXPIRED);
// Clear downloaded time
ZeroMemory(&MsgInfo.ftDownloaded, sizeof(FILETIME));
// Update the Record
IF_FAILEXIT(hr = pDB->UpdateRecord(&MsgInfo));
// Count Removed Read
(*pcRemovedRead)++;
}
}
// Free Current
pDB->FreeRecord(&MsgInfo);
}
exit:
// Cleanup
pDB->CloseRowset(&hRowset);
pDB->FreeRecord(&MsgInfo);
// Log File
if (g_pCleanLog && FAILED(hr))
{
// Write
g_pCleanLog->WriteLog(LOGFILE_DB, _MSG("CleanupNewsgroup(%s) Returned: 0x%08X", pszFile, hr));
}
// Done
return(hr);
}
// --------------------------------------------------------------------------------
// CleanupJunkMail
// --------------------------------------------------------------------------------
HRESULT CleanupJunkMail(LPCSTR pszFile, IDatabase *pDB, LPDWORD pcJunkDeleted)
{
// Locals
HRESULT hr = S_OK;
FILETIME ftCurrent = {0};
DWORD cDeleteDays = 0;
IDatabase *pUidlDB = NULL;
HROWSET hRowset = NULL;
MESSAGEINFO MsgInfo = {0};
DWORD cClients = 0;
// Trace
TraceCall("CleanupJunkMail");
// Is there anything to do?
if ((0 == DwGetOption(OPT_FILTERJUNK)) || (0 == DwGetOption(OPT_DELETEJUNK)) || (0 == (g_dwAthenaMode & MODE_JUNKMAIL)))
{
hr = S_FALSE;
goto exit;
}
// Get Current Time
GetSystemTimeAsFileTime(&ftCurrent);
// Get the Number of Days in which to expire messages
cDeleteDays = DwGetOption(OPT_DELETEJUNKDAYS);
// Open the UIDL Cache
IF_FAILEXIT(hr = OpenUidlCache(&pUidlDB));
// Create a Rowset
IF_FAILEXIT(hr = pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
// Loop
while (S_OK == pDB->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL))
{
// If I'm not the only client, then abort the cleanup
IF_FAILEXIT(hr = pDB->GetClientCount(&cClients));
// Better be 1
if (cClients != 1)
{
hr = DB_E_COMPACT_PREEMPTED;
goto exit;
}
// Abort
if (S_OK != g_cProgress.Update(1))
{
hr = STORE_E_OPERATION_CANCELED;
goto exit;
}
// Has the message been around long enough?
if (cDeleteDays <= (UlDateDiff(&MsgInfo.ftDownloaded, &ftCurrent) / SECONDS_INA_DAY))
{
// Count Deleted
(*pcJunkDeleted)++;
// Delete the message
IF_FAILEXIT(hr = DeleteMessageFromStore(&MsgInfo, pDB, pUidlDB));
}
// Free Current
pDB->FreeRecord(&MsgInfo);
}
hr = S_OK;
exit:
// Cleanup
SafeRelease(pUidlDB);
pDB->CloseRowset(&hRowset);
pDB->FreeRecord(&MsgInfo);
// Log File
if (g_pCleanLog && FAILED(hr))
{
// Write
g_pCleanLog->WriteLog(LOGFILE_DB, _MSG("CleanupJunkMail(%s) Returned: 0x%08X", pszFile, hr));
}
// Done
return(hr);
}