Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

956 lines
27 KiB

  1. //--------------------------------------------------------------------------
  2. // Cleanup.cpp
  3. //--------------------------------------------------------------------------
  4. #include "pch.hxx"
  5. #include "cleanup.h"
  6. #include "goptions.h"
  7. #include "shlwapi.h"
  8. #include "storutil.h"
  9. #include "xpcomm.h"
  10. #include "shared.h"
  11. #include "syncop.h"
  12. #include "storsync.h"
  13. #include "instance.h"
  14. #include "demand.h"
  15. // --------------------------------------------------------------------------------
  16. // Strings
  17. // --------------------------------------------------------------------------------
  18. static const LPSTR g_szCleanupWndProc = "OEStoreCleanupThread";
  19. static const LPSTR c_szRegLastStoreCleaned = "Last Store Cleaned";
  20. static const LPSTR c_szRegLastFolderCleaned = "Last Folder Cleaned";
  21. // --------------------------------------------------------------------------------
  22. // STOREFILETYPE
  23. // --------------------------------------------------------------------------------
  24. typedef enum tagSTOREFILETYPE {
  25. STORE_FILE_HEADERS,
  26. STORE_FILE_FOLDERS,
  27. STORE_FILE_POP3UIDL,
  28. STORE_FILE_OFFLINE,
  29. STORE_FILE_LAST
  30. } STOREFILETYPE;
  31. // --------------------------------------------------------------------------------
  32. // Globals
  33. // --------------------------------------------------------------------------------
  34. static BOOL g_fShutdown=FALSE;
  35. // --------------------------------------------------------------------------------
  36. // CCompactProgress
  37. // --------------------------------------------------------------------------------
  38. class CCompactProgress : public IDatabaseProgress
  39. {
  40. public:
  41. STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv) { return TraceResult(E_NOTIMPL); }
  42. STDMETHODIMP_(ULONG) AddRef(void) { return(2); }
  43. STDMETHODIMP_(ULONG) Release(void) { return(1); }
  44. STDMETHODIMP Update(DWORD cCount) { return(g_fShutdown ? hrUserCancel : S_OK); }
  45. };
  46. // --------------------------------------------------------------------------------
  47. // Globals
  48. // --------------------------------------------------------------------------------
  49. static DWORD g_dwCleanupThreadId=0;
  50. static HANDLE g_hCleanupThread=NULL;
  51. static HWND g_hwndStoreCleanup=NULL;
  52. static UINT_PTR g_uDelayTimerId=0;
  53. static HROWSET g_hCleanupRowset=NULL;
  54. static BOOL g_fWorking=FALSE;
  55. static STOREFILETYPE g_tyCurrentFile=STORE_FILE_LAST;
  56. static ILogFile *g_pCleanLog=NULL;
  57. static CCompactProgress g_cProgress;
  58. // --------------------------------------------------------------------------------
  59. // Timer Constants
  60. // --------------------------------------------------------------------------------
  61. #define IDT_START_CYCLE (WM_USER + 1)
  62. #define IDT_CLEANUP_FOLDER (WM_USER + 2)
  63. #define CYCLE_INTERVAL (1000 * 60 * 30)
  64. // --------------------------------------------------------------------------------
  65. // CLEANUPTRHEADCREATE
  66. // --------------------------------------------------------------------------------
  67. typedef struct tagCLEANUPTRHEADCREATE {
  68. HRESULT hrResult;
  69. HANDLE hEvent;
  70. } CLEANUPTRHEADCREATE, *LPCLEANUPTRHEADCREATE;
  71. // --------------------------------------------------------------------------------
  72. // Prototypes
  73. // --------------------------------------------------------------------------------
  74. DWORD StoreCleanupThreadEntry(LPDWORD pdwParam);
  75. LRESULT CALLBACK StoreCleanupWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  76. HRESULT CleanupStoreInitializeCycle(HWND hwnd);
  77. HRESULT CleanupCurrentFolder(HWND hwnd);
  78. HRESULT SetNextCleanupFolder(HWND hwnd);
  79. HRESULT StartCleanupCycle(HWND hwnd);
  80. HRESULT CleanupNewsgroup(LPCSTR pszFile, IDatabase *pDB, LPDWORD pcRemovedRead, LPDWORD pcRemovedExpired);
  81. HRESULT CleanupJunkMail(LPCSTR pszFile, IDatabase *pDB, LPDWORD pcJunkDeleted);
  82. //--------------------------------------------------------------------------
  83. // RegisterWindowClass
  84. //--------------------------------------------------------------------------
  85. HRESULT RegisterWindowClass(LPCSTR pszClass, WNDPROC pfnWndProc)
  86. {
  87. // Locals
  88. HRESULT hr=S_OK;
  89. WNDCLASS WindowClass;
  90. // Tracing
  91. TraceCall("RegisterWindowClass");
  92. // Register the Window Class
  93. if (0 != GetClassInfo(g_hInst, pszClass, &WindowClass))
  94. goto exit;
  95. // Zero the object
  96. ZeroMemory(&WindowClass, sizeof(WNDCLASS));
  97. // Initialize the Window Class
  98. WindowClass.lpfnWndProc = pfnWndProc;
  99. WindowClass.hInstance = g_hInst;
  100. WindowClass.lpszClassName = pszClass;
  101. // Register the Class
  102. if (0 == RegisterClass(&WindowClass))
  103. {
  104. hr = TraceResult(E_FAIL);
  105. goto exit;
  106. }
  107. exit:
  108. // Done
  109. return hr;
  110. }
  111. //--------------------------------------------------------------------------
  112. // CreateNotifyWindow
  113. //--------------------------------------------------------------------------
  114. HRESULT CreateNotifyWindow(LPCSTR pszClass, LPVOID pvParam, HWND *phwndNotify)
  115. {
  116. // Locals
  117. HRESULT hr=S_OK;
  118. HWND hwnd;
  119. // Tracing
  120. TraceCall("CreateNotifyWindow");
  121. // Invalid ARg
  122. Assert(pszClass && phwndNotify);
  123. // Initialize
  124. *phwndNotify = NULL;
  125. // Create the Window
  126. hwnd = CreateWindowEx(WS_EX_TOPMOST, pszClass, pszClass, WS_POPUP, 0, 0, 0, 0, NULL, NULL, g_hInst, (LPVOID)pvParam);
  127. // Failure
  128. if (NULL == hwnd)
  129. {
  130. hr = TraceResult(E_FAIL);
  131. goto exit;
  132. }
  133. // Set Return
  134. *phwndNotify = hwnd;
  135. exit:
  136. // Done
  137. return hr;
  138. }
  139. // --------------------------------------------------------------------------------
  140. // DelayedStartStoreCleanup
  141. // --------------------------------------------------------------------------------
  142. void CALLBACK DelayedStartStoreCleanup(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
  143. {
  144. // Trace
  145. TraceCall("DelayedStartStoreCleanup");
  146. // Must have a timer
  147. Assert(g_uDelayTimerId);
  148. // Kill the Timer
  149. KillTimer(NULL, g_uDelayTimerId);
  150. // Set g_uDelayTimerId
  151. g_uDelayTimerId = 0;
  152. // Call this function with zero delay...
  153. StartBackgroundStoreCleanup(0);
  154. }
  155. // --------------------------------------------------------------------------------
  156. // StartBackgroundStoreCleanup
  157. // --------------------------------------------------------------------------------
  158. HRESULT StartBackgroundStoreCleanup(DWORD dwDelaySeconds)
  159. {
  160. // Locals
  161. HRESULT hr=S_OK;
  162. CLEANUPTRHEADCREATE Create={0};
  163. // Trace
  164. TraceCall("StartBackgroundStoreCleanup");
  165. // Already Running ?
  166. if (NULL != g_hCleanupThread)
  167. return(S_OK);
  168. // If dwDelaySeconds is NOT zero, then lets start this function later
  169. if (0 != dwDelaySeconds)
  170. {
  171. // Trace
  172. TraceInfo(_MSG("Delayed start store cleanup in %d seconds.", dwDelaySeconds));
  173. // Set a timer to call this the delay function a little bit later
  174. g_uDelayTimerId = SetTimer(NULL, 0, (dwDelaySeconds * 1000), DelayedStartStoreCleanup);
  175. // Failure
  176. if (0 == g_uDelayTimerId)
  177. {
  178. hr = TraceResult(E_FAIL);
  179. goto exit;
  180. }
  181. // Done
  182. return(S_OK);
  183. }
  184. // Trace
  185. TraceInfo("Starting store cleanup.");
  186. // Initialize
  187. Create.hrResult = S_OK;
  188. // Create an Event to synchonize creation
  189. Create.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  190. if (NULL == Create.hEvent)
  191. {
  192. hr = TrapError(E_OUTOFMEMORY);
  193. goto exit;
  194. }
  195. // Create the inetmail thread
  196. g_hCleanupThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StoreCleanupThreadEntry, &Create, 0, &g_dwCleanupThreadId);
  197. if (NULL == g_hCleanupThread)
  198. {
  199. hr = TrapError(E_OUTOFMEMORY);
  200. goto exit;
  201. }
  202. // Wait for StoreCleanupThreadEntry to signal the event
  203. WaitForSingleObject(Create.hEvent, INFINITE);
  204. // Failure
  205. if (FAILED(Create.hrResult))
  206. {
  207. // Close
  208. SafeCloseHandle(g_hCleanupThread);
  209. // Reset Globals
  210. g_hCleanupThread = NULL;
  211. g_dwCleanupThreadId = 0;
  212. // Return
  213. hr = TrapError(Create.hrResult);
  214. // Done
  215. goto exit;
  216. }
  217. exit:
  218. // Cleanup
  219. SafeCloseHandle(Create.hEvent);
  220. // Done
  221. return(hr);
  222. }
  223. // ------------------------------------------------------------------------------------
  224. // CloseBackgroundStoreCleanup
  225. // ------------------------------------------------------------------------------------
  226. HRESULT CloseBackgroundStoreCleanup(void)
  227. {
  228. // Trace
  229. TraceCall("CloseBackgroundStoreCleanup");
  230. // Trace
  231. TraceInfo("Terminating Store Cleanup thread.");
  232. // Kill the Timer
  233. if (g_uDelayTimerId)
  234. {
  235. KillTimer(NULL, g_uDelayTimerId);
  236. g_uDelayTimerId = 0;
  237. }
  238. // Invalid Arg
  239. if (0 != g_dwCleanupThreadId)
  240. {
  241. // Assert
  242. Assert(g_hCleanupThread && FALSE == g_fShutdown);
  243. // Set Shutdown bit
  244. g_fShutdown = TRUE;
  245. // Post quit message
  246. PostThreadMessage(g_dwCleanupThreadId, WM_QUIT, 0, 0);
  247. // Wait for event to become signaled
  248. WaitForSingleObject(g_hCleanupThread, INFINITE);
  249. }
  250. // Validate
  251. Assert(NULL == g_hwndStoreCleanup && 0 == g_dwCleanupThreadId);
  252. // If we have a handle
  253. if (NULL != g_hCleanupThread)
  254. {
  255. // Close the thread handle
  256. CloseHandle(g_hCleanupThread);
  257. // Reset Globals
  258. g_hCleanupThread = NULL;
  259. }
  260. // Done
  261. return(S_OK);
  262. }
  263. // --------------------------------------------------------------------------------
  264. // InitializeCleanupLogFile
  265. // --------------------------------------------------------------------------------
  266. HRESULT InitializeCleanupLogFile(void)
  267. {
  268. // Locals
  269. HRESULT hr=S_OK;
  270. CHAR szLogFile[MAX_PATH];
  271. CHAR szStoreRoot[MAX_PATH];
  272. // Trace
  273. TraceCall("InitializeCleanupLogFile");
  274. // Better not have a log file yet
  275. Assert(NULL == g_pCleanLog);
  276. // Open Log File
  277. IF_FAILEXIT(hr = GetStoreRootDirectory(szStoreRoot, ARRAYSIZE(szStoreRoot)));
  278. // MakeFilePath to cleanup.log
  279. IF_FAILEXIT(hr = MakeFilePath(szStoreRoot, "cleanup.log", c_szEmpty, szLogFile, ARRAYSIZE(szLogFile)));
  280. // Open the LogFile
  281. IF_FAILEXIT(hr = CreateLogFile(g_hLocRes, szLogFile, "CLEANUP", 65536, &g_pCleanLog,
  282. FILE_SHARE_READ | FILE_SHARE_WRITE));
  283. // Write the Store root
  284. g_pCleanLog->WriteLog(LOGFILE_DB, szStoreRoot);
  285. exit:
  286. // Done
  287. return(hr);
  288. }
  289. // --------------------------------------------------------------------------------
  290. // StoreCleanupThreadEntry
  291. // --------------------------------------------------------------------------------
  292. DWORD StoreCleanupThreadEntry(LPDWORD pdwParam)
  293. {
  294. // Locals
  295. HRESULT hr=S_OK;
  296. MSG msg;
  297. DWORD dw;
  298. DWORD cb;
  299. LPCLEANUPTRHEADCREATE pCreate;
  300. // Trace
  301. TraceCall("StoreCleanupThreadEntry");
  302. // We better have a parameter
  303. Assert(pdwParam);
  304. // Cast to create info
  305. pCreate = (LPCLEANUPTRHEADCREATE)pdwParam;
  306. // Initialize OLE
  307. hr = CoInitialize(NULL);
  308. if (FAILED(hr))
  309. {
  310. TraceResult(hr);
  311. pCreate->hrResult = hr;
  312. SetEvent(pCreate->hEvent);
  313. return(1);
  314. }
  315. // Reset Shutdown Bit
  316. g_fShutdown = FALSE;
  317. // OpenCleanupLogFile
  318. InitializeCleanupLogFile();
  319. // Registery the window class
  320. IF_FAILEXIT(hr = RegisterWindowClass(g_szCleanupWndProc, StoreCleanupWindowProc));
  321. // Create the notification window
  322. IF_FAILEXIT(hr = CreateNotifyWindow(g_szCleanupWndProc, NULL, &g_hwndStoreCleanup));
  323. // Success
  324. pCreate->hrResult = S_OK;
  325. // Run at a low-priority
  326. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
  327. // Set Event
  328. SetEvent(pCreate->hEvent);
  329. // Pump Messages
  330. while (GetMessage(&msg, NULL, 0, 0))
  331. {
  332. TranslateMessage(&msg);
  333. DispatchMessage(&msg);
  334. }
  335. // Kill the timer
  336. if (g_uDelayTimerId)
  337. {
  338. KillTimer(NULL, g_uDelayTimerId);
  339. g_uDelayTimerId = 0;
  340. }
  341. // Kill the Current Timer
  342. KillTimer(g_hwndStoreCleanup, IDT_CLEANUP_FOLDER);
  343. // Kill the timer
  344. KillTimer(g_hwndStoreCleanup, IDT_START_CYCLE);
  345. // Kill the Window
  346. DestroyWindow(g_hwndStoreCleanup);
  347. g_hwndStoreCleanup = NULL;
  348. g_dwCleanupThreadId = 0;
  349. // Release LogFile
  350. SafeRelease(g_pCleanLog);
  351. exit:
  352. // Failure
  353. if (FAILED(hr))
  354. {
  355. pCreate->hrResult = hr;
  356. SetEvent(pCreate->hEvent);
  357. }
  358. // Uninit
  359. CoUninitialize();
  360. // Done
  361. return 1;
  362. }
  363. // --------------------------------------------------------------------------------
  364. // StoreCleanupWindowProc
  365. // --------------------------------------------------------------------------------
  366. LRESULT CALLBACK StoreCleanupWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  367. {
  368. // Trace
  369. TraceCall("StoreCleanupWindowProc");
  370. // Switch
  371. switch(msg)
  372. {
  373. // OnCreate
  374. case WM_CREATE:
  375. // Set the time to start the first cycle in one second
  376. SetTimer(hwnd, IDT_START_CYCLE, 1000, NULL);
  377. // Done
  378. return(0);
  379. // OnTime
  380. case WM_TIMER:
  381. // Cleanup Folder Timer
  382. if (IDT_CLEANUP_FOLDER == wParam)
  383. {
  384. // Kill the Current Timer
  385. KillTimer(hwnd, IDT_CLEANUP_FOLDER);
  386. // Cleanup the Next Folder
  387. CleanupCurrentFolder(hwnd);
  388. }
  389. // Start new cleanup cycle
  390. else if (IDT_START_CYCLE == wParam)
  391. {
  392. // Kill the timer
  393. KillTimer(hwnd, IDT_START_CYCLE);
  394. // Start a new cycle
  395. StartCleanupCycle(hwnd);
  396. }
  397. // Done
  398. return(0);
  399. // OnDestroy
  400. case WM_DESTROY:
  401. // Close the Current Rowset
  402. g_pLocalStore->CloseRowset(&g_hCleanupRowset);
  403. // Done
  404. return(0);
  405. }
  406. // Deletegate
  407. return DefWindowProc(hwnd, msg, wParam, lParam);
  408. }
  409. // --------------------------------------------------------------------------------
  410. // StartCleanupCycle
  411. // --------------------------------------------------------------------------------
  412. HRESULT StartCleanupCycle(HWND hwnd)
  413. {
  414. // Locals
  415. HRESULT hr=S_OK;
  416. // Trace
  417. TraceCall("StartCleanupCycle");
  418. // Validate State
  419. Assert(g_pLocalStore && NULL == g_hCleanupRowset);
  420. // Logfile
  421. if (g_pCleanLog)
  422. {
  423. // WriteLogFile
  424. g_pCleanLog->WriteLog(LOGFILE_DB, "Starting Background Cleanup Cycle...");
  425. }
  426. // Create a Store Rowset
  427. IF_FAILEXIT(hr = g_pLocalStore->CreateRowset(IINDEX_SUBSCRIBED, NOFLAGS, &g_hCleanupRowset));
  428. // Set state
  429. g_tyCurrentFile = STORE_FILE_HEADERS;
  430. // Cleanup this folder...
  431. SetTimer(hwnd, IDT_CLEANUP_FOLDER, 100, NULL);
  432. exit:
  433. // Done
  434. return(hr);
  435. }
  436. // --------------------------------------------------------------------------------
  437. // CleanupCurrentFolder
  438. // --------------------------------------------------------------------------------
  439. HRESULT CleanupCurrentFolder(HWND hwnd)
  440. {
  441. // Locals
  442. HRESULT hr=S_OK;
  443. LPCSTR pszFile=NULL;
  444. FOLDERTYPE tyFolder=FOLDER_INVALID;
  445. SPECIALFOLDER tySpecial=FOLDER_NOTSPECIAL;
  446. FOLDERINFO Folder={0};
  447. DWORD cRecords;
  448. DWORD cbAllocated;
  449. DWORD cbFreed;
  450. DWORD cbStreams;
  451. DWORD cbFile;
  452. DWORD dwWasted;
  453. DWORD dwCompactAt;
  454. DWORD cRemovedRead=0;
  455. DWORD cRemovedExpired=0;
  456. DWORD cJunkDeleted=0;
  457. IDatabase *pDB=NULL;
  458. IMessageFolder *pFolderObject=NULL;
  459. // Trace
  460. TraceCall("CleanupCurrentFolder");
  461. // Validate
  462. Assert(g_pLocalStore);
  463. // Get Next Folder
  464. if (STORE_FILE_HEADERS == g_tyCurrentFile)
  465. {
  466. // Better have a rowset
  467. Assert(g_hCleanupRowset);
  468. // Get a Folder
  469. hr = g_pLocalStore->QueryRowset(g_hCleanupRowset, 1, (LPVOID *)&Folder, NULL);
  470. if (FAILED(hr) || S_FALSE == hr)
  471. {
  472. // Don with Current Cycle
  473. g_pLocalStore->CloseRowset(&g_hCleanupRowset);
  474. // Set g_tyCurrentFile
  475. g_tyCurrentFile = STORE_FILE_FOLDERS;
  476. }
  477. // Otherwise...
  478. else if (FOLDERID_ROOT == Folder.idFolder || ISFLAGSET(Folder.dwFlags, FOLDER_SERVER))
  479. {
  480. // Goto Next
  481. goto exit;
  482. }
  483. // Otherwise
  484. else
  485. {
  486. // Set some stuff
  487. pszFile = Folder.pszFile;
  488. tyFolder = Folder.tyFolder;
  489. tySpecial = Folder.tySpecial;
  490. // If no folder file, then jump to exit
  491. if (NULL == pszFile)
  492. goto exit;
  493. // Open the folder object
  494. if (FAILED(g_pLocalStore->OpenFolder(Folder.idFolder, NULL, OPEN_FOLDER_NOCREATE, &pFolderObject)))
  495. goto exit;
  496. // Get the Database
  497. pFolderObject->GetDatabase(&pDB);
  498. }
  499. }
  500. // If something other than a folder
  501. if (STORE_FILE_HEADERS != g_tyCurrentFile)
  502. {
  503. // Locals
  504. LPCTABLESCHEMA pSchema=NULL;
  505. LPCSTR pszName=NULL;
  506. CHAR szRootDir[MAX_PATH + MAX_PATH];
  507. CHAR szFilePath[MAX_PATH + MAX_PATH];
  508. // Folders
  509. if (STORE_FILE_FOLDERS == g_tyCurrentFile)
  510. {
  511. pszName = pszFile = c_szFoldersFile;
  512. pSchema = &g_FolderTableSchema;
  513. g_tyCurrentFile = STORE_FILE_POP3UIDL;
  514. }
  515. // Pop3uidl
  516. else if (STORE_FILE_POP3UIDL == g_tyCurrentFile)
  517. {
  518. pszName = pszFile = c_szPop3UidlFile;
  519. pSchema = &g_UidlTableSchema;
  520. g_tyCurrentFile = STORE_FILE_OFFLINE;
  521. }
  522. // Offline.dbx
  523. else if (STORE_FILE_OFFLINE == g_tyCurrentFile)
  524. {
  525. pszName = pszFile = c_szOfflineFile;
  526. pSchema = &g_SyncOpTableSchema;
  527. g_tyCurrentFile = STORE_FILE_LAST;
  528. }
  529. // Otherwise, we are done
  530. else if (STORE_FILE_LAST == g_tyCurrentFile)
  531. {
  532. // Set time to start next cycle
  533. SetTimer(hwnd, IDT_START_CYCLE, CYCLE_INTERVAL, NULL);
  534. // Done
  535. return(S_OK);
  536. }
  537. // Validate
  538. Assert(pSchema && pszName);
  539. // No File
  540. if (FIsEmptyA(pszFile))
  541. goto exit;
  542. // Get Root Directory
  543. IF_FAILEXIT(hr = GetStoreRootDirectory(szRootDir, ARRAYSIZE(szRootDir)));
  544. // Make File Path
  545. IF_FAILEXIT(hr = MakeFilePath(szRootDir, pszFile, c_szEmpty, szFilePath, ARRAYSIZE(szFilePath)));
  546. // If the File Exists?
  547. if (FALSE == PathFileExists(szFilePath))
  548. goto exit;
  549. // Open a Database Object on the file
  550. IF_FAILEXIT(hr = g_pDBSession->OpenDatabase(szFilePath, OPEN_DATABASE_NORESET, pSchema, NULL, &pDB));
  551. }
  552. // Not Working
  553. g_fWorking = TRUE;
  554. // Get Record Count
  555. IF_FAILEXIT(hr = pDB->GetRecordCount(IINDEX_PRIMARY, &cRecords));
  556. // If this is a news folder, and I'm the only person with it open...
  557. if (FOLDER_NEWS == tyFolder)
  558. {
  559. // Cleanup Newgroup
  560. CleanupNewsgroup(pszFile, pDB, &cRemovedRead, &cRemovedExpired);
  561. }
  562. // If this is the junk mail folder
  563. if ((FOLDER_LOCAL == tyFolder) && (FOLDER_JUNK == tySpecial))
  564. {
  565. // Cleanup Junk Mail folder
  566. CleanupJunkMail(pszFile, pDB, &cJunkDeleted);
  567. }
  568. // Get Size Information...
  569. IF_FAILEXIT(hr = pDB->GetSize(&cbFile, &cbAllocated, &cbFreed, &cbStreams));
  570. // Wasted
  571. dwWasted = cbAllocated > 0 ? ((cbFreed * 100) / cbAllocated) : 0;
  572. // Get Option about when to compact
  573. dwCompactAt = DwGetOption(OPT_CACHECOMPACTPER);
  574. // Trace
  575. if (g_pCleanLog)
  576. {
  577. // Write
  578. 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));
  579. }
  580. // If less than 25% wasted space and there is more than a meg allocated
  581. if (dwWasted < dwCompactAt)
  582. goto exit;
  583. // Compact
  584. hr = pDB->Compact((IDatabaseProgress *)&g_cProgress, COMPACT_PREEMPTABLE | COMPACT_YIELD);
  585. // Log Result
  586. if (g_pCleanLog && S_OK != hr)
  587. {
  588. // Write
  589. g_pCleanLog->WriteLog(LOGFILE_DB, _MSG("IDatabase::Compact(%s) Returned: 0x%08X", pszFile, hr));
  590. }
  591. exit:
  592. // Cleanup
  593. SafeRelease(pDB);
  594. SafeRelease(pFolderObject);
  595. g_pLocalStore->FreeRecord(&Folder);
  596. // Not Working
  597. g_fWorking = FALSE;
  598. // ShutDown
  599. if (FALSE == g_fShutdown)
  600. {
  601. // Compute Next CleanupFolder
  602. SetTimer(hwnd, IDT_CLEANUP_FOLDER, 100, NULL);
  603. }
  604. // Done
  605. return(hr);
  606. }
  607. // --------------------------------------------------------------------------------
  608. // CleanupNewsgroup
  609. // --------------------------------------------------------------------------------
  610. HRESULT CleanupNewsgroup(LPCSTR pszFile, IDatabase *pDB, LPDWORD pcRemovedRead,
  611. LPDWORD pcRemovedExpired)
  612. {
  613. // Locals
  614. HRESULT hr=S_OK;
  615. DWORD cExpireDays;
  616. BOOL fRemoveExpired=FALSE;
  617. BOOL fRemoveRead=FALSE;
  618. DWORD cClients;
  619. FILETIME ftCurrent;
  620. HROWSET hRowset=NULL;
  621. MESSAGEINFO MsgInfo={0};
  622. // Trace
  623. TraceCall("CleanupNewsgroup");
  624. // Get Current Time
  625. GetSystemTimeAsFileTime(&ftCurrent);
  626. // Get the Number of Days in which to expire messages
  627. cExpireDays = DwGetOption(OPT_CACHEDELETEMSGS);
  628. // If the option is not off, set the flag
  629. fRemoveExpired = (OPTION_OFF == cExpireDays) ? FALSE : TRUE;
  630. // Remove Read ?
  631. fRemoveRead = (FALSE != DwGetOption(OPT_CACHEREAD) ? TRUE : FALSE);
  632. // Nothing to do
  633. if (FALSE == fRemoveExpired && FALSE == fRemoveRead)
  634. goto exit;
  635. // Create a Rowset
  636. IF_FAILEXIT(hr = pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
  637. // Loop
  638. while (S_OK == pDB->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL))
  639. {
  640. // If I'm not the only client, then abort the cleanup
  641. IF_FAILEXIT(hr = pDB->GetClientCount(&cClients));
  642. // Better be 1
  643. if (cClients != 1)
  644. {
  645. hr = DB_E_COMPACT_PREEMPTED;
  646. goto exit;
  647. }
  648. // Abort
  649. if (S_OK != g_cProgress.Update(1))
  650. {
  651. hr = STORE_E_OPERATION_CANCELED;
  652. goto exit;
  653. }
  654. // Only if this message has a body
  655. if (!ISFLAGSET(MsgInfo.dwFlags, ARF_KEEPBODY) && !ISFLAGSET(MsgInfo.dwFlags, ARF_WATCH) && ISFLAGSET(MsgInfo.dwFlags, ARF_HASBODY) && MsgInfo.faStream)
  656. {
  657. // Otherwise, if expiring...
  658. if (TRUE == fRemoveExpired && (UlDateDiff(&MsgInfo.ftDownloaded, &ftCurrent) / SECONDS_INA_DAY) >= cExpireDays)
  659. {
  660. // Delete this message
  661. IF_FAILEXIT(hr = pDB->DeleteRecord(&MsgInfo));
  662. // Count Removed Expired
  663. (*pcRemovedExpired)++;
  664. }
  665. // Removing Read and this message is read ?
  666. else if (TRUE == fRemoveRead && ISFLAGSET(MsgInfo.dwFlags, ARF_READ))
  667. {
  668. // Delete the Stream
  669. pDB->DeleteStream(MsgInfo.faStream);
  670. // No More Stream
  671. MsgInfo.faStream = 0;
  672. // Fixup the Record
  673. FLAGCLEAR(MsgInfo.dwFlags, ARF_HASBODY | ARF_ARTICLE_EXPIRED);
  674. // Clear downloaded time
  675. ZeroMemory(&MsgInfo.ftDownloaded, sizeof(FILETIME));
  676. // Update the Record
  677. IF_FAILEXIT(hr = pDB->UpdateRecord(&MsgInfo));
  678. // Count Removed Read
  679. (*pcRemovedRead)++;
  680. }
  681. }
  682. // Free Current
  683. pDB->FreeRecord(&MsgInfo);
  684. }
  685. exit:
  686. // Cleanup
  687. pDB->CloseRowset(&hRowset);
  688. pDB->FreeRecord(&MsgInfo);
  689. // Log File
  690. if (g_pCleanLog && FAILED(hr))
  691. {
  692. // Write
  693. g_pCleanLog->WriteLog(LOGFILE_DB, _MSG("CleanupNewsgroup(%s) Returned: 0x%08X", pszFile, hr));
  694. }
  695. // Done
  696. return(hr);
  697. }
  698. // --------------------------------------------------------------------------------
  699. // CleanupJunkMail
  700. // --------------------------------------------------------------------------------
  701. HRESULT CleanupJunkMail(LPCSTR pszFile, IDatabase *pDB, LPDWORD pcJunkDeleted)
  702. {
  703. // Locals
  704. HRESULT hr = S_OK;
  705. FILETIME ftCurrent = {0};
  706. DWORD cDeleteDays = 0;
  707. IDatabase *pUidlDB = NULL;
  708. HROWSET hRowset = NULL;
  709. MESSAGEINFO MsgInfo = {0};
  710. DWORD cClients = 0;
  711. // Trace
  712. TraceCall("CleanupJunkMail");
  713. // Is there anything to do?
  714. if ((0 == DwGetOption(OPT_FILTERJUNK)) || (0 == DwGetOption(OPT_DELETEJUNK)) || (0 == (g_dwAthenaMode & MODE_JUNKMAIL)))
  715. {
  716. hr = S_FALSE;
  717. goto exit;
  718. }
  719. // Get Current Time
  720. GetSystemTimeAsFileTime(&ftCurrent);
  721. // Get the Number of Days in which to expire messages
  722. cDeleteDays = DwGetOption(OPT_DELETEJUNKDAYS);
  723. // Open the UIDL Cache
  724. IF_FAILEXIT(hr = OpenUidlCache(&pUidlDB));
  725. // Create a Rowset
  726. IF_FAILEXIT(hr = pDB->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset));
  727. // Loop
  728. while (S_OK == pDB->QueryRowset(hRowset, 1, (LPVOID *)&MsgInfo, NULL))
  729. {
  730. // If I'm not the only client, then abort the cleanup
  731. IF_FAILEXIT(hr = pDB->GetClientCount(&cClients));
  732. // Better be 1
  733. if (cClients != 1)
  734. {
  735. hr = DB_E_COMPACT_PREEMPTED;
  736. goto exit;
  737. }
  738. // Abort
  739. if (S_OK != g_cProgress.Update(1))
  740. {
  741. hr = STORE_E_OPERATION_CANCELED;
  742. goto exit;
  743. }
  744. // Has the message been around long enough?
  745. if (cDeleteDays <= (UlDateDiff(&MsgInfo.ftDownloaded, &ftCurrent) / SECONDS_INA_DAY))
  746. {
  747. // Count Deleted
  748. (*pcJunkDeleted)++;
  749. // Delete the message
  750. IF_FAILEXIT(hr = DeleteMessageFromStore(&MsgInfo, pDB, pUidlDB));
  751. }
  752. // Free Current
  753. pDB->FreeRecord(&MsgInfo);
  754. }
  755. hr = S_OK;
  756. exit:
  757. // Cleanup
  758. SafeRelease(pUidlDB);
  759. pDB->CloseRowset(&hRowset);
  760. pDB->FreeRecord(&MsgInfo);
  761. // Log File
  762. if (g_pCleanLog && FAILED(hr))
  763. {
  764. // Write
  765. g_pCleanLog->WriteLog(LOGFILE_DB, _MSG("CleanupJunkMail(%s) Returned: 0x%08X", pszFile, hr));
  766. }
  767. // Done
  768. return(hr);
  769. }