Source code of Windows XP (NT5)
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.

981 lines
29 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: purge.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include <cscuiext.h> // CSCUIRemoveFolderFromCache
  13. #include "purge.h"
  14. #include "cscutils.h"
  15. #include "msgbox.h"
  16. #include "resource.h"
  17. #include "security.h"
  18. #include "util.h"
  19. #include "strings.h"
  20. //
  21. // This is also defined in cscui\dll\pch.h
  22. // If you change it there you must change it here and vice versa.
  23. //
  24. #define FLAG_CSC_HINT_PIN_ADMIN FLAG_CSC_HINT_PIN_SYSTEM
  25. //
  26. // Purge confirmation dialog.
  27. // The user can set which files are purged from the cache.
  28. //
  29. class CConfirmPurgeDialog
  30. {
  31. public:
  32. CConfirmPurgeDialog(void)
  33. : m_hInstance(NULL),
  34. m_hwnd(NULL),
  35. m_hwndLV(NULL),
  36. m_pSel(NULL)
  37. { }
  38. ~CConfirmPurgeDialog(void)
  39. { if (NULL != m_hwnd) DestroyWindow(m_hwnd); }
  40. int Run(HINSTANCE hInstance, HWND hwndParent, CCachePurgerSel *pSel);
  41. private:
  42. HINSTANCE m_hInstance;
  43. HWND m_hwnd;
  44. HWND m_hwndLV;
  45. CCachePurgerSel *m_pSel; // Ptr to destination for selection info.
  46. static INT_PTR CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  47. void OnInitDialog(HWND hwnd);
  48. void OnDestroy(void);
  49. void OnOk(void);
  50. void OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam);
  51. BOOL LVSetItemData(HWND hwndLV, int i, LPARAM lParam);
  52. LPARAM LVGetItemData(HWND hwndLV, int i);
  53. int LVAddItem(HWND hwndLV, LPCTSTR pszItem);
  54. };
  55. inline ULONGLONG
  56. MakeULongLong(DWORD dwLow, DWORD dwHigh)
  57. {
  58. return ((ULONGLONG)(((DWORD)(dwLow)) | ((LONGLONG)((DWORD)(dwHigh))) << 32));
  59. }
  60. inline bool
  61. IsDirty(const CscFindData& cfd)
  62. {
  63. return 0 != (FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY & cfd.dwStatus);
  64. }
  65. inline bool
  66. IsSuperHidden(const CscFindData& cfd)
  67. {
  68. const DWORD dwSuperHidden = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
  69. return (cfd.fd.dwFileAttributes & dwSuperHidden) == dwSuperHidden;
  70. }
  71. inline bool
  72. OthersHaveAccess(const CscFindData& cfd)
  73. {
  74. return CscAccessOther(cfd.dwStatus);
  75. }
  76. CCachePurger::CCachePurger(
  77. const CCachePurgerSel& sel,
  78. LPFNPURGECALLBACK pfnCbk,
  79. LPVOID pvCbkData
  80. ) : m_ullBytesToScan(0),
  81. m_ullBytesScanned(0),
  82. m_ullBytesToDelete(0),
  83. m_ullBytesDeleted(0),
  84. m_ullFileBytes(0),
  85. m_dwPhase(0),
  86. m_cFilesToScan(0),
  87. m_cFilesScanned(0),
  88. m_cFilesToDelete(0),
  89. m_cFilesDeleted(0),
  90. m_iFile(0),
  91. m_dwFileAttributes(0),
  92. m_dwResult(0),
  93. m_hmutexPurgeInProg(NULL),
  94. m_pszFile(NULL),
  95. m_pvCbkData(pvCbkData),
  96. m_bWillDelete(FALSE),
  97. m_pfnCbk(pfnCbk),
  98. m_bIsValid(true),
  99. m_sel(sel),
  100. m_bDelPinned(0 != (PURGE_FLAG_PINNED & sel.Flags())),
  101. m_bDelUnpinned(0 != (PURGE_FLAG_UNPINNED & sel.Flags())),
  102. m_bIgnoreAccess(0 != (PURGE_IGNORE_ACCESS & sel.Flags())),
  103. m_bUserIsAnAdmin(boolify(IsCurrentUserAnAdminMember()))
  104. {
  105. //
  106. // Let the world know we're purging files from the cache.
  107. // In particular, our overlay handler in cscui\dll\shellex.cpp needs
  108. // to disable it's auto-pin code whenever we're purging. If we didn't
  109. // do this AND the source folder is open during the purge, we get a
  110. // nasty race condition between our shell notifications for purging
  111. // and the overlay handler's auto-pin code. We'll delete a file
  112. // and send a notification. The shell updates the icon overlay.
  113. // Our handler sees that the parent folder is pinned so it re-pins
  114. // the purged file which restores the file in the folder. This ends
  115. // up resulting in a very nasty infinite loop. Those interested
  116. // should call IsPurgeInProgress() to determine if the purge is
  117. // in progress (see cscui\dll\util.cpp). [brianau - 11/01/99]
  118. //
  119. m_hmutexPurgeInProg = CreateMutex(NULL, TRUE, c_szPurgeInProgMutex);
  120. }
  121. CCachePurger::~CCachePurger(
  122. void
  123. )
  124. {
  125. if (NULL != m_hmutexPurgeInProg)
  126. {
  127. ReleaseMutex(m_hmutexPurgeInProg);
  128. CloseHandle(m_hmutexPurgeInProg);
  129. }
  130. }
  131. //
  132. // Deletes a directory and all it's contents according to the PURGE_FLAG_XXXXX flag
  133. // bits set by the caller of PurgeCache().
  134. // This function recursively traverses the cache file hierarchy in a post-order fashion.
  135. // Directory nodes are deleted after all children have been deleted.
  136. // If the caller of PurgeCache provided a callback function, it is called
  137. // after each deletion. If the callback function returns FALSE, the traversal
  138. // operation is terminated.
  139. //
  140. // pstrPath - Address of path string containing the path of the directory to be
  141. // deleted. This object is used to contain the working path throughout
  142. // the tree traversal.
  143. //
  144. // dwPhase - PURGE_PHASE_SCAN - Scanning for file totals.
  145. // PURGE_PHASE_DELETE - Deleting files.
  146. //
  147. // bShareIsOffline - The share is offline.
  148. //
  149. // Returns: true = Continue traversal.
  150. // false = User cancelled via callback. Terminate traversal.
  151. //
  152. //
  153. bool
  154. CCachePurger::ProcessDirectory(
  155. LPTSTR pszPath,
  156. DWORD dwPhase,
  157. bool bShareIsOffline
  158. )
  159. {
  160. bool bContinue = true;
  161. CscFindData cfd;
  162. CscFindHandle hFind(CacheFindFirst(pszPath, m_sel.UserSid(), &cfd));
  163. if (hFind.IsValidHandle())
  164. {
  165. do
  166. {
  167. bool bIsDirectory = (0 != (FILE_ATTRIBUTE_DIRECTORY & cfd.fd.dwFileAttributes));
  168. //
  169. // Create full path to this file/folder.
  170. //
  171. ::PathAppend(pszPath, cfd.fd.cFileName);
  172. if (bIsDirectory)
  173. {
  174. //
  175. // It's a directory. Recursively delete it's contents.
  176. //
  177. bContinue = ProcessDirectory(pszPath, dwPhase, bShareIsOffline);
  178. }
  179. if (bContinue)
  180. {
  181. bool bPinned = (0 != ((FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_ADMIN) & cfd.dwHintFlags));
  182. //
  183. // The decision to delete a file has several criteria. I've tried to break this
  184. // up using inlines and member variables to make it more understandable and minimize
  185. // maintenance bugs.
  186. // The logic for deletion is this:
  187. //
  188. // bDelete = false;
  189. // If (pinned AND deleting pinned) OR (not pinned and deleting unpinned) then
  190. // If super_hidden then
  191. // bDelete = true;
  192. // else
  193. // If (not locally dirty) then
  194. // If (ignore access) then
  195. // bDelete = true;
  196. // else
  197. // If (user is an admin) then
  198. // bDelete = true;
  199. // else
  200. // if (others have NO access) then
  201. // bDelete = true;
  202. // endif
  203. // endif
  204. // endif
  205. // endif
  206. // endif
  207. // endif
  208. bool bDelete = ((bPinned && m_bDelPinned) || (!bPinned && m_bDelUnpinned)) &&
  209. (IsSuperHidden(cfd) ||
  210. (!IsDirty(cfd) &&
  211. (m_bIgnoreAccess ||
  212. (m_bUserIsAnAdmin || !OthersHaveAccess(cfd)))));
  213. m_pszFile = pszPath;
  214. m_dwFileAttributes = cfd.fd.dwFileAttributes;
  215. m_ullFileBytes = MakeULongLong(cfd.fd.nFileSizeLow, cfd.fd.nFileSizeHigh);
  216. m_bWillDelete = bDelete;
  217. if (PURGE_PHASE_SCAN == dwPhase)
  218. {
  219. if (!bIsDirectory && m_pfnCbk)
  220. {
  221. //
  222. // Exclude directories from the file and byte counts.
  223. //
  224. if (bDelete)
  225. {
  226. m_cFilesToDelete++;
  227. m_ullBytesToDelete += m_ullFileBytes;
  228. }
  229. m_cFilesScanned++;
  230. m_ullBytesScanned += m_ullFileBytes;
  231. m_dwResult = ERROR_SUCCESS;
  232. bContinue = boolify((*m_pfnCbk)(this));
  233. m_iFile++;
  234. }
  235. }
  236. else if (PURGE_PHASE_DELETE == dwPhase && bDelete)
  237. {
  238. LONG lShellEvent = SHCNE_UPDATEITEM;
  239. m_dwResult = CscDelete(pszPath);
  240. if (ERROR_SUCCESS == m_dwResult)
  241. {
  242. if (!bIsDirectory)
  243. {
  244. m_cFilesDeleted++;
  245. m_ullBytesDeleted += m_ullFileBytes;
  246. }
  247. if (bShareIsOffline)
  248. {
  249. lShellEvent = bIsDirectory ? SHCNE_RMDIR : SHCNE_DELETE;
  250. }
  251. }
  252. else
  253. {
  254. if (ERROR_ACCESS_DENIED == m_dwResult)
  255. {
  256. //
  257. // This is a little weird. CscDelete
  258. // returns ERROR_ACCESS_DENIED if there's
  259. // a handle open on the file. Set the
  260. // code to ERROR_BUSY so we know to handle
  261. // this as a special case.
  262. //
  263. m_dwResult = ERROR_BUSY;
  264. }
  265. //
  266. // NTRAID#NTBUG9-213486-2001/01/29-jeffreys
  267. //
  268. // CscDelete failed. Make sure it's unpinned, so
  269. // it doesn't get the icon overlay anymore.
  270. //
  271. // This can happen if there is a handle open on a file,
  272. // or if there is a view open on a directory, in which
  273. // case there is a change notification handle open. It
  274. // will also happen (later) for any parent directories,
  275. // since they are not empty.
  276. //
  277. CSCUnpinFile(pszPath,
  278. FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_INHERIT_USER,
  279. NULL,
  280. NULL,
  281. NULL);
  282. }
  283. ShellChangeNotify(pszPath, &cfd.fd, FALSE, lShellEvent);
  284. if (!bIsDirectory && m_pfnCbk)
  285. {
  286. bContinue = boolify((*m_pfnCbk)(this));
  287. m_iFile++;
  288. }
  289. }
  290. }
  291. ::PathRemoveFileSpec(pszPath);
  292. }
  293. while(bContinue && CacheFindNext(hFind, &cfd));
  294. }
  295. ::PathRemoveBackslash(pszPath);
  296. return bContinue;
  297. }
  298. //
  299. // Public function for purging cache contents.
  300. //
  301. HRESULT
  302. CCachePurger::Process(
  303. DWORD dwPhase
  304. )
  305. {
  306. HRESULT hr = NOERROR;
  307. if (!m_bIsValid)
  308. return E_OUTOFMEMORY; // Failed ctor.
  309. m_dwPhase = dwPhase;
  310. if (PURGE_PHASE_SCAN == dwPhase)
  311. {
  312. //
  313. // At start of scanning phase, get the max bytes and file count
  314. // from the CSC database. This will let us provide meaningful
  315. // progress data during the scanning phase.
  316. //
  317. ULARGE_INTEGER ulTotalBytes = {0, 0};
  318. ULARGE_INTEGER ulUsedBytes = {0, 0};
  319. DWORD dwTotalFiles = 0;
  320. DWORD dwTotalDirs = 0;
  321. TCHAR szVolume[MAX_PATH];
  322. CSCGetSpaceUsage(szVolume,
  323. ARRAYSIZE(szVolume),
  324. &ulTotalBytes.HighPart,
  325. &ulTotalBytes.LowPart,
  326. &ulUsedBytes.HighPart,
  327. &ulUsedBytes.LowPart,
  328. &dwTotalFiles,
  329. &dwTotalDirs);
  330. m_cFilesToScan = dwTotalFiles + dwTotalDirs;
  331. m_ullBytesToScan = ulTotalBytes.QuadPart;
  332. m_ullBytesToDelete = 0;
  333. m_ullBytesDeleted = 0;
  334. m_ullBytesScanned = 0;
  335. m_ullFileBytes = 0;
  336. m_cFilesToDelete = 0;
  337. m_cFilesDeleted = 0;
  338. m_cFilesScanned = 0;
  339. m_dwFileAttributes = 0;
  340. m_dwResult = 0;
  341. m_pszFile = NULL;
  342. m_bWillDelete = false;
  343. }
  344. m_iFile = 0; // Reset this for each phase.
  345. bool bContinue = true;
  346. CscFindData cfd;
  347. TCHAR szPath[MAX_PATH * 2];
  348. if (0 < m_sel.ShareCount())
  349. {
  350. //
  351. // Delete 1+ (but not all) shares.
  352. //
  353. for (int i = 0; i < m_sel.ShareCount(); i++)
  354. {
  355. lstrcpyn(szPath, m_sel.ShareName(i), ARRAYSIZE(szPath));
  356. cfd.dwStatus = 0;
  357. CSCQueryFileStatus(szPath, &cfd.dwStatus, NULL, NULL);
  358. bContinue = ProcessDirectory(szPath,
  359. dwPhase,
  360. boolify(FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus));
  361. if (PURGE_PHASE_DELETE == dwPhase)
  362. {
  363. LONG lShellEvent = SHCNE_UPDATEITEM;
  364. if (ERROR_SUCCESS == CscDelete(szPath))
  365. {
  366. if (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus)
  367. {
  368. lShellEvent = SHCNE_RMDIR;
  369. }
  370. }
  371. else
  372. {
  373. //
  374. // NTRAID#NTBUG9-213486-2001/01/29-jeffreys
  375. //
  376. // If unable to delete, make sure it's unpinned.
  377. //
  378. CSCUnpinFile(szPath,
  379. FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_INHERIT_USER,
  380. NULL,
  381. NULL,
  382. NULL);
  383. }
  384. ZeroMemory(&cfd.fd, sizeof(cfd.fd));
  385. cfd.fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  386. lstrcpyn(cfd.fd.cFileName, szPath, ARRAYSIZE(cfd.fd.cFileName));
  387. ShellChangeNotify(szPath, &cfd.fd, FALSE, lShellEvent);
  388. }
  389. }
  390. if (PURGE_PHASE_DELETE == dwPhase)
  391. {
  392. //
  393. // On the DELETE phase always try to remove any empty
  394. // share entries from the database. CSCDelete will
  395. // harmlessly fail if the share entry cannot be deleted.
  396. //
  397. CscFindHandle hFind(CacheFindFirst(NULL, m_sel.UserSid(), &cfd));
  398. if (hFind.IsValidHandle())
  399. {
  400. do
  401. {
  402. // Don't unpin on failure here. The user wants to keep these.
  403. if (ERROR_SUCCESS == CscDelete(cfd.fd.cFileName))
  404. {
  405. ShellChangeNotify(cfd.fd.cFileName,
  406. &cfd.fd,
  407. FALSE,
  408. (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus) ? SHCNE_RMDIR : SHCNE_UPDATEITEM);
  409. }
  410. }
  411. while(CacheFindNext(hFind, &cfd));
  412. }
  413. }
  414. }
  415. else
  416. {
  417. //
  418. // Delete all shares.
  419. //
  420. CscFindHandle hFind(CacheFindFirst(NULL, m_sel.UserSid(), &cfd));
  421. if (hFind.IsValidHandle())
  422. {
  423. do
  424. {
  425. lstrcpyn(szPath, cfd.fd.cFileName, ARRAYSIZE(szPath));
  426. bContinue = ProcessDirectory(szPath,
  427. dwPhase,
  428. boolify(FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus));
  429. if (PURGE_PHASE_DELETE == dwPhase)
  430. {
  431. LONG lShellEvent = SHCNE_UPDATEITEM;
  432. if (ERROR_SUCCESS == CscDelete(szPath))
  433. {
  434. if (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & cfd.dwStatus)
  435. {
  436. lShellEvent = SHCNE_RMDIR;
  437. }
  438. }
  439. else
  440. {
  441. //
  442. // NTRAID#NTBUG9-213486-2001/01/29-jeffreys
  443. //
  444. // If unable to delete, make sure it's unpinned.
  445. //
  446. CSCUnpinFile(szPath,
  447. FLAG_CSC_HINT_PIN_USER | FLAG_CSC_HINT_PIN_INHERIT_USER,
  448. NULL,
  449. NULL,
  450. NULL);
  451. }
  452. ShellChangeNotify(szPath, &cfd.fd, FALSE, lShellEvent);
  453. }
  454. }
  455. while(bContinue && CacheFindNext(hFind, &cfd));
  456. }
  457. }
  458. //
  459. // Flush any pending notifications
  460. //
  461. ShellChangeNotify(NULL, TRUE);
  462. return hr;
  463. }
  464. //
  465. // Displays a modal dialog to get cache purging confirmation from the
  466. // user. Let's user indicate if they want to purge only temp files
  467. // from the cache or both temp and pinned.
  468. //
  469. // Returns PURGE_FLAG_XXXX flags and a list of share names
  470. // in the CCachePurgerSel object.
  471. //
  472. void
  473. CCachePurger::AskUserWhatToPurge(
  474. HWND hwndParent,
  475. CCachePurgerSel *pSel
  476. )
  477. {
  478. CConfirmPurgeDialog dlg;
  479. dlg.Run(GetModuleHandle(TEXT("cscui.dll")), hwndParent, pSel);
  480. }
  481. //
  482. // Returns:
  483. // 0 = User cancelled.
  484. // 1 = User pressed OK.
  485. //
  486. // Returns PURGE_FLAG_XXXX flags and a list of share names
  487. // in the CCachePurgerSel object.
  488. //
  489. int
  490. CConfirmPurgeDialog::Run(
  491. HINSTANCE hInstance,
  492. HWND hwndParent,
  493. CCachePurgerSel *pSel // We don't "own" this. Merely a WRITE reference.
  494. )
  495. {
  496. TraceAssert(NULL != hInstance);
  497. TraceAssert(NULL != hwndParent);
  498. TraceAssert(NULL != pSel);
  499. m_hInstance = hInstance;
  500. m_pSel = pSel;
  501. int iResult = (int)DialogBoxParam(hInstance,
  502. MAKEINTRESOURCE(IDD_CONFIRM_PURGE),
  503. hwndParent,
  504. DlgProc,
  505. (LPARAM)this);
  506. if (-1 == iResult)
  507. {
  508. Trace((TEXT("Error %d creating delete confirmation dialog"), GetLastError()));
  509. }
  510. return iResult;
  511. }
  512. INT_PTR CALLBACK
  513. CConfirmPurgeDialog::DlgProc(
  514. HWND hwnd,
  515. UINT message,
  516. WPARAM wParam,
  517. LPARAM lParam
  518. )
  519. {
  520. CConfirmPurgeDialog *pThis = reinterpret_cast<CConfirmPurgeDialog *>(GetWindowLongPtr(hwnd, DWLP_USER));
  521. switch(message)
  522. {
  523. case WM_INITDIALOG:
  524. SetWindowLongPtr(hwnd, DWLP_USER, (INT_PTR)lParam);
  525. pThis = reinterpret_cast<CConfirmPurgeDialog *>(lParam);
  526. TraceAssert(NULL != pThis);
  527. pThis->OnInitDialog(hwnd);
  528. return TRUE;
  529. case WM_ENDSESSION:
  530. EndDialog(hwnd, IDNO);
  531. return FALSE;
  532. case WM_COMMAND:
  533. {
  534. int iResult = 0; // Assume [Cancel]
  535. switch(LOWORD(wParam))
  536. {
  537. case IDOK:
  538. iResult = 1;
  539. pThis->OnOk();
  540. //
  541. // Fall through...
  542. //
  543. case IDCANCEL:
  544. EndDialog(hwnd, iResult);
  545. return FALSE;
  546. }
  547. }
  548. break;
  549. case WM_SETTINGCHANGE:
  550. case WM_SYSCOLORCHANGE:
  551. pThis->OnSettingChange(message, wParam, lParam);
  552. break;
  553. case WM_DESTROY:
  554. pThis->OnDestroy();
  555. pThis->m_hwnd = NULL;
  556. return FALSE;
  557. default:
  558. break;
  559. }
  560. return FALSE;
  561. }
  562. void
  563. CConfirmPurgeDialog::OnInitDialog(
  564. HWND hwnd
  565. )
  566. {
  567. TraceAssert(NULL != hwnd);
  568. RECT rc;
  569. m_hwnd = hwnd;
  570. m_hwndLV = GetDlgItem(hwnd, IDC_LIST_PURGE);
  571. CheckDlgButton(hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED, BST_CHECKED);
  572. CheckDlgButton(hwnd, IDC_RBN_CONFIRMPURGE_ALL, BST_UNCHECKED);
  573. //
  574. // Turn on checkboxes in the listview.
  575. //
  576. ListView_SetExtendedListViewStyleEx(m_hwndLV, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
  577. //
  578. // Add the single column to the listview.
  579. //
  580. GetClientRect(m_hwndLV, &rc);
  581. LV_COLUMN col = { LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM,
  582. LVCFMT_LEFT,
  583. rc.right - rc.left,
  584. TEXT(""),
  585. 0,
  586. 0 };
  587. ListView_InsertColumn(m_hwndLV, 0, &col);
  588. //
  589. // Create the image list for the listview.
  590. //
  591. HIMAGELIST hSmallImages = ImageList_Create(16, 16, ILC_MASK, 1, 0);
  592. if (NULL != hSmallImages)
  593. {
  594. HICON hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SHARE));
  595. if (NULL != hIcon)
  596. {
  597. ImageList_AddIcon(hSmallImages, hIcon);
  598. }
  599. ListView_SetImageList(m_hwndLV, hSmallImages, LVSIL_SMALL);
  600. }
  601. //
  602. // Fill "shares" list with share names.
  603. //
  604. CscFindData cfd;
  605. CscFindHandle hFind(CacheFindFirst(NULL, &cfd));
  606. if (hFind.IsValidHandle())
  607. {
  608. LPITEMIDLIST pidl = NULL;
  609. SHFILEINFO sfi;
  610. CSCSHARESTATS ss;
  611. CSCGETSTATSINFO si = { SSEF_NONE, SSUF_TOTAL, true, false };
  612. do
  613. {
  614. _GetShareStatisticsForUser(cfd.fd.cFileName, &si, &ss);
  615. if (0 < ss.cTotal)
  616. {
  617. ZeroMemory(&sfi, sizeof(sfi));
  618. if (SUCCEEDED(SHSimpleIDListFromFindData(cfd.fd.cFileName, &cfd.fd, &pidl)))
  619. {
  620. SHGetFileInfo((LPCTSTR)pidl,
  621. 0,
  622. &sfi,
  623. sizeof(sfi),
  624. SHGFI_PIDL | SHGFI_DISPLAYNAME);
  625. SHFree(pidl);
  626. }
  627. int iItem = LVAddItem(m_hwndLV, sfi.szDisplayName);
  628. if (0 <= iItem)
  629. {
  630. //
  631. // All items are initially checked.
  632. //
  633. ListView_SetCheckState(m_hwndLV, iItem, TRUE);
  634. //
  635. // Each item's lParam contains a pointer to the
  636. // UNC path allocated on the heap. Must be deleted
  637. // in OnDestroy().
  638. //
  639. LPTSTR pszFileName = StrDup(cfd.fd.cFileName);
  640. if (NULL != pszFileName)
  641. {
  642. if (!LVSetItemData(m_hwndLV, iItem, (LPARAM)pszFileName))
  643. {
  644. LocalFree(pszFileName);
  645. }
  646. }
  647. }
  648. }
  649. }
  650. while(CacheFindNext(hFind, &cfd));
  651. }
  652. if (0 == ListView_GetItemCount(m_hwndLV))
  653. {
  654. //
  655. // No items are in the listview.
  656. // Disable all of the controls, hide the "OK" button and
  657. // change the "Cancel" button to "Close".
  658. //
  659. const UINT rgidCtls[] = { IDC_TXT_CONFIRMPURGE3,
  660. IDC_RBN_CONFIRMPURGE_UNPINNED,
  661. IDC_RBN_CONFIRMPURGE_ALL,
  662. IDC_LIST_PURGE,
  663. IDOK};
  664. ShowWindow(GetDlgItem(m_hwnd, IDOK), SW_HIDE);
  665. for (int i = 0; i < ARRAYSIZE(rgidCtls); i++)
  666. {
  667. EnableWindow(GetDlgItem(m_hwnd, rgidCtls[i]), FALSE);
  668. }
  669. TCHAR szText[MAX_PATH];
  670. LoadString(m_hInstance, IDS_BTN_TITLE_CLOSE, szText, ARRAYSIZE(szText));
  671. SetWindowText(GetDlgItem(m_hwnd, IDCANCEL), szText);
  672. //
  673. // Replace the listview's caption with something like "There are
  674. // no offline files to delete".
  675. //
  676. LoadString(m_hInstance, IDS_TXT_NO_FILES_TO_DELETE, szText, ARRAYSIZE(szText));
  677. SetWindowText(GetDlgItem(m_hwnd, IDC_TXT_CONFIRMPURGE2), szText);
  678. //
  679. // Uncheck both radio buttons.
  680. //
  681. CheckDlgButton(m_hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED, BST_UNCHECKED);
  682. CheckDlgButton(m_hwnd, IDC_RBN_CONFIRMPURGE_ALL, BST_UNCHECKED);
  683. }
  684. }
  685. LPARAM
  686. CConfirmPurgeDialog::LVGetItemData(
  687. HWND hwndLV,
  688. int i
  689. )
  690. {
  691. LVITEM item;
  692. item.mask = LVIF_PARAM;
  693. item.iItem = i;
  694. item.iSubItem = 0;
  695. if (ListView_GetItem(hwndLV, &item))
  696. {
  697. return item.lParam;
  698. }
  699. return 0;
  700. }
  701. BOOL
  702. CConfirmPurgeDialog::LVSetItemData(
  703. HWND hwndLV,
  704. int i,
  705. LPARAM lParam
  706. )
  707. {
  708. LVITEM item;
  709. item.mask = LVIF_PARAM;
  710. item.iItem = i;
  711. item.iSubItem = 0;
  712. item.lParam = lParam;
  713. return ListView_SetItem(hwndLV, &item);
  714. }
  715. int
  716. CConfirmPurgeDialog::LVAddItem(
  717. HWND hwndLV,
  718. LPCTSTR pszItem
  719. )
  720. {
  721. LVITEM item;
  722. item.mask = LVIF_TEXT;
  723. item.pszText = (LPTSTR)pszItem;
  724. item.iSubItem = 0;
  725. item.iItem = ListView_GetItemCount(hwndLV);
  726. return ListView_InsertItem(hwndLV, &item);
  727. }
  728. void
  729. CConfirmPurgeDialog::OnOk(
  730. void
  731. )
  732. {
  733. const int cShares = ListView_GetItemCount(m_hwndLV);
  734. for (int i = 0; i < cShares; i++)
  735. {
  736. if (0 != ListView_GetCheckState(m_hwndLV, i))
  737. {
  738. m_pSel->AddShareName((LPCTSTR)LVGetItemData(m_hwndLV, i));
  739. }
  740. }
  741. if (0 < m_pSel->ShareCount())
  742. {
  743. m_pSel->SetFlags((BST_CHECKED == IsDlgButtonChecked(m_hwnd, IDC_RBN_CONFIRMPURGE_UNPINNED)) ?
  744. PURGE_FLAG_UNPINNED : PURGE_FLAG_ALL);
  745. }
  746. else
  747. {
  748. m_pSel->SetFlags(PURGE_FLAG_NONE);
  749. }
  750. }
  751. void
  752. CConfirmPurgeDialog::OnDestroy(
  753. void
  754. )
  755. {
  756. if (NULL != m_hwndLV)
  757. {
  758. const int cShares = ListView_GetItemCount(m_hwndLV);
  759. for (int i = 0; i < cShares; i++)
  760. {
  761. LPTSTR psz = (LPTSTR)LVGetItemData(m_hwndLV, i);
  762. if (NULL != psz)
  763. {
  764. LocalFree(psz);
  765. }
  766. }
  767. }
  768. }
  769. void
  770. CConfirmPurgeDialog::OnSettingChange(
  771. UINT uMsg,
  772. WPARAM wParam,
  773. LPARAM lParam
  774. )
  775. {
  776. if (NULL != m_hwndLV)
  777. SendMessage(m_hwndLV, uMsg, wParam, lParam);
  778. }
  779. CCachePurgerSel::~CCachePurgerSel(
  780. void
  781. )
  782. {
  783. if (NULL != m_hdpaShares)
  784. {
  785. const int cShares = DPA_GetPtrCount(m_hdpaShares);
  786. for (int i = 0; i < cShares; i++)
  787. {
  788. LPTSTR psz = (LPTSTR)DPA_GetPtr(m_hdpaShares, i);
  789. if (NULL != psz)
  790. {
  791. LocalFree(psz);
  792. }
  793. }
  794. DPA_Destroy(m_hdpaShares);
  795. }
  796. if (NULL != m_psidUser)
  797. {
  798. LocalFree(m_psidUser);
  799. }
  800. }
  801. BOOL
  802. CCachePurgerSel::SetUserSid(
  803. PSID psid
  804. )
  805. {
  806. if (NULL != m_psidUser)
  807. {
  808. LocalFree(m_psidUser);
  809. m_psidUser = NULL;
  810. }
  811. if (NULL != psid && IsValidSid(psid))
  812. {
  813. DWORD cbSid = GetLengthSid(psid);
  814. PSID psidNew = (PSID)LocalAlloc(LPTR, cbSid);
  815. if (NULL != psidNew)
  816. {
  817. if (!CopySid(cbSid, psidNew, psid))
  818. {
  819. LocalFree(psidNew);
  820. psidNew = NULL;
  821. }
  822. m_psidUser = psidNew;
  823. }
  824. }
  825. return NULL != m_psidUser;
  826. }
  827. BOOL
  828. CCachePurgerSel::AddShareName(
  829. LPCTSTR pszShare
  830. )
  831. {
  832. //
  833. // Be tolerant of a NULL pszShare pointer.
  834. //
  835. if (NULL != m_hdpaShares && NULL != pszShare)
  836. {
  837. LPTSTR pszCopy = StrDup(pszShare);
  838. if (NULL != pszCopy)
  839. {
  840. if (-1 != DPA_AppendPtr(m_hdpaShares, pszCopy))
  841. {
  842. return true;
  843. }
  844. LocalFree(pszCopy);
  845. }
  846. }
  847. return false;
  848. }
  849. typedef struct _RemoveFolderCBData
  850. {
  851. PFN_CSCUIRemoveFolderCallback pfnCB;
  852. LPARAM lParam;
  853. } RemoveFolderCBData, *PRemoveFolderCBData;
  854. BOOL CALLBACK
  855. _RemoveFolderCallback(CCachePurger *pPurger)
  856. {
  857. PRemoveFolderCBData pcbdata = (PRemoveFolderCBData)pPurger->CallbackData();
  858. if (pcbdata->pfnCB)
  859. return pcbdata->pfnCB(pPurger->FileName(), pcbdata->lParam);
  860. return TRUE;
  861. }
  862. STDAPI
  863. CSCUIRemoveFolderFromCache(LPCWSTR pszFolder,
  864. DWORD /*dwReserved*/, // can use for flags
  865. PFN_CSCUIRemoveFolderCallback pfnCB,
  866. LPARAM lParam)
  867. {
  868. RemoveFolderCBData cbdata = { pfnCB, lParam };
  869. CCachePurgerSel sel;
  870. sel.SetFlags(PURGE_FLAG_ALL | PURGE_IGNORE_ACCESS);
  871. sel.AddShareName(pszFolder);
  872. CCachePurger purger(sel, _RemoveFolderCallback, &cbdata);
  873. return purger.Delete();
  874. }