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.

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