//-------------------------------------------------------------------------- // 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); }