957 lines
27 KiB
C++
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);
|
|
}
|
|
|