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.

653 lines
18 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: enum.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include <shlwapip.h> // QITAB, QISearch
  13. #include <shsemip.h> // ILFree(), etc
  14. #include "folder.h"
  15. #include "security.h"
  16. //
  17. // Create a single entry in the server status cache.
  18. //
  19. CServerStatusCache::CEntry::CEntry(
  20. LPCTSTR pszServer,
  21. DWORD dwStatus
  22. ) : m_pszServer(StrDup(pszServer)),
  23. m_dwStatus(dwStatus)
  24. {
  25. }
  26. //
  27. // Destroy a single entry in the server status cache.
  28. //
  29. CServerStatusCache::CEntry::~CEntry(
  30. void
  31. )
  32. {
  33. if (NULL != m_pszServer)
  34. {
  35. LocalFree(m_pszServer);
  36. }
  37. }
  38. //
  39. // Destroy the server status cache.
  40. //
  41. CServerStatusCache::~CServerStatusCache(
  42. void
  43. )
  44. {
  45. if (NULL != m_hdpa)
  46. {
  47. //
  48. // Delete each entry in the DPA then destroy the
  49. // DPA itself.
  50. //
  51. int cEntries = DPA_GetPtrCount(m_hdpa);
  52. for (int i = 0; i < cEntries; i++)
  53. {
  54. delete (CEntry *)DPA_GetPtr(m_hdpa, i);
  55. }
  56. DPA_Destroy(m_hdpa);
  57. }
  58. }
  59. //
  60. // Add a share's status to the cache. We strip the UNC path to it's
  61. // bare server name then add the status to the cache. If there's
  62. // no existing entry we just add it. If there is an existing entry,
  63. // we bitwise OR the status bits in with the existing entry. This way
  64. // the status of the server is the summation of the status of all
  65. // it's shares.
  66. //
  67. bool
  68. CServerStatusCache::AddShareStatus(
  69. LPCTSTR pszShare,
  70. DWORD dwShareStatus
  71. )
  72. {
  73. bool bResult = true;
  74. TCHAR szServer[MAX_PATH];
  75. CEntry *pEntry = FindEntry(ServerFromUNC(pszShare, szServer, ARRAYSIZE(szServer)));
  76. if (NULL != pEntry)
  77. {
  78. //
  79. // Found existing server entry for this share. Merge in the
  80. // status bits for this share.
  81. //
  82. pEntry->AddStatus(dwShareStatus);
  83. }
  84. else
  85. {
  86. //
  87. // No existing entry for this share's server.
  88. //
  89. if (NULL == m_hdpa)
  90. {
  91. //
  92. // No DPA exists yet. Create one.
  93. // We delay creation of the DPA until we really need one.
  94. //
  95. m_hdpa = DPA_Create(8);
  96. }
  97. if (NULL != m_hdpa)
  98. {
  99. //
  100. // We have a DPA. Create a new entry for this share's server
  101. // and add it to the DPA.
  102. //
  103. pEntry = new CEntry(szServer, dwShareStatus);
  104. if (NULL != pEntry)
  105. {
  106. if (!pEntry->IsValid() || -1 == DPA_AppendPtr(m_hdpa, pEntry))
  107. {
  108. //
  109. // One of the following happened:
  110. // 1. Failure allocating server name in CEntry obj.
  111. // 2. Failure adding CEntry obj ptr to DPA.
  112. //
  113. delete pEntry;
  114. bResult = false;
  115. }
  116. }
  117. }
  118. else
  119. {
  120. bResult = false; // DPA creation failed.
  121. }
  122. }
  123. return bResult;
  124. }
  125. //
  126. // Obtain the CSC status bits for a given server.
  127. // This function assumes the pszUNC arg is a valid UNC path.
  128. //
  129. DWORD
  130. CServerStatusCache::GetServerStatus(
  131. LPCTSTR pszUNC
  132. )
  133. {
  134. TCHAR szServer[MAX_PATH];
  135. CEntry *pEntry = FindEntry(ServerFromUNC(pszUNC, szServer, ARRAYSIZE(szServer)));
  136. if (NULL == pEntry)
  137. {
  138. //
  139. // No entry for this server. Scan the CSC cache and pick up any new
  140. // servers added. Since the lifetime of this server cache is only for a single
  141. // enumeration, we should have to do this only once. However, if for some
  142. // reason, something gets added to the CSC cache while we're opening the viewer,
  143. // this code path will pick up the new server entry.
  144. //
  145. WIN32_FIND_DATA fd;
  146. DWORD dwStatus = 0;
  147. CCscFindHandle hFind = CacheFindFirst(NULL, &fd, &dwStatus, NULL, NULL, NULL);
  148. if (hFind.IsValid())
  149. {
  150. do
  151. {
  152. AddShareStatus(fd.cFileName, dwStatus);
  153. }
  154. while(CacheFindNext(hFind, &fd, &dwStatus, NULL, NULL, NULL));
  155. }
  156. //
  157. // Now that we have rescanned the CSC cache, try it again.
  158. //
  159. pEntry = FindEntry(szServer);
  160. }
  161. return pEntry ? pEntry->GetStatus() : 0;
  162. }
  163. //
  164. // Find a single entry in the server cache.
  165. // Assumes pszServer is a raw server name (not UNC).
  166. // Returns NULL if no match found.
  167. //
  168. CServerStatusCache::CEntry *
  169. CServerStatusCache::FindEntry(
  170. LPCTSTR pszServer
  171. )
  172. {
  173. CEntry *pEntry = NULL;
  174. if (NULL != m_hdpa)
  175. {
  176. int cEntries = DPA_GetPtrCount(m_hdpa);
  177. for (int i = 0; i < cEntries; i++)
  178. {
  179. CEntry *pe = (CEntry *)DPA_GetPtr(m_hdpa, i);
  180. if (0 == lstrcmpi(pe->GetServer(), pszServer))
  181. {
  182. pEntry = pe;
  183. break;
  184. }
  185. }
  186. }
  187. return pEntry;
  188. }
  189. LPTSTR
  190. CServerStatusCache::ServerFromUNC(
  191. LPCTSTR pszShare,
  192. LPTSTR pszServer,
  193. UINT cchServer
  194. )
  195. {
  196. LPTSTR pszReturn = pszServer; // Remember for return.
  197. cchServer--; // Leave room for terminating nul.
  198. while(*pszShare && TEXT('\\') == *pszShare)
  199. pszShare++;
  200. while(*pszShare && TEXT('\\') != *pszShare && cchServer--)
  201. *pszServer++ = *pszShare++;
  202. *pszServer = TEXT('\0');
  203. return pszReturn;
  204. }
  205. STDMETHODIMP COfflineFilesEnum::QueryInterface(REFIID riid, void **ppv)
  206. {
  207. static const QITAB qit[] = {
  208. QITABENT(COfflineFilesEnum, IEnumIDList),
  209. { 0 },
  210. };
  211. return QISearch(this, qit, riid, ppv);
  212. }
  213. STDMETHODIMP_ (ULONG) COfflineFilesEnum::AddRef()
  214. {
  215. return InterlockedIncrement(&_cRef);
  216. }
  217. STDMETHODIMP_ (ULONG) COfflineFilesEnum::Release()
  218. {
  219. if (InterlockedDecrement(&_cRef))
  220. return _cRef;
  221. delete this;
  222. return 0;
  223. }
  224. COfflineFilesEnum::COfflineFilesEnum(DWORD grfFlags, COfflineFilesFolder *pfolder)
  225. {
  226. _cRef = 1;
  227. //
  228. // The minimum size of the buffer must be MAX_PATH.
  229. // The enumeration code is designed to grow it as needed.
  230. //
  231. _cchPathBuf = MAX_PATH;
  232. _pszPath = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * _cchPathBuf);
  233. if (NULL != _pszPath)
  234. *_pszPath = TEXT('\0');
  235. else
  236. _cchPathBuf = 0;
  237. _grfFlags = grfFlags,
  238. _pfolder = pfolder;
  239. _pfolder->AddRef();
  240. _dwServerStatus = 0;
  241. _hdsaFolderPathInfo = DSA_Create(sizeof(FolderPathInfo), 10);
  242. //
  243. // Determine if we should be showing system and/or hidden files.
  244. //
  245. _bShowHiddenFiles = boolify(ShowHidden());
  246. _bShowSuperHiddenFiles = boolify(ShowSuperHidden());
  247. _bUserIsAdmin = boolify(IsCurrentUserAnAdminMember());
  248. DllAddRef();
  249. }
  250. COfflineFilesEnum::~COfflineFilesEnum()
  251. {
  252. if (_pfolder)
  253. _pfolder->Release();
  254. Reset();
  255. if (_hdsaFolderPathInfo)
  256. {
  257. int cPaths = DSA_GetItemCount(_hdsaFolderPathInfo);
  258. FolderPathInfo fpi;
  259. for (int i = 0; i < cPaths; i++)
  260. {
  261. if (DSA_GetItem(_hdsaFolderPathInfo, i, &fpi) && NULL != fpi.pszPath)
  262. LocalFree(fpi.pszPath);
  263. }
  264. DSA_Destroy(_hdsaFolderPathInfo);
  265. }
  266. if (NULL != _pszPath)
  267. LocalFree(_pszPath);
  268. DllRelease();
  269. }
  270. //
  271. // Since we're not throwing exceptions, clients must call this after ctor
  272. // to verify allocations succeeded.
  273. //
  274. bool
  275. COfflineFilesEnum::IsValid(
  276. void
  277. ) const
  278. {
  279. return (NULL != _hdsaFolderPathInfo) && (NULL != _pszPath);
  280. }
  281. bool
  282. COfflineFilesEnum::PopFolderPathInfo(
  283. FolderPathInfo *pfpi
  284. )
  285. {
  286. bool bResult = false;
  287. TraceAssert(NULL != _hdsaFolderPathInfo);
  288. int iItem = DSA_GetItemCount(_hdsaFolderPathInfo) - 1;
  289. if ((0 <= iItem) && DSA_GetItem(_hdsaFolderPathInfo, iItem, pfpi))
  290. {
  291. DSA_DeleteItem(_hdsaFolderPathInfo, iItem);
  292. bResult = true;
  293. }
  294. return bResult;
  295. }
  296. //
  297. // Build complete path to folder in a heap allocation and push it onto
  298. // stack of saved folder paths.
  299. // Returns false if memory can't be allocated for path.
  300. //
  301. bool
  302. COfflineFilesEnum::SaveFolderPath(
  303. LPCTSTR pszRoot,
  304. LPCTSTR pszFolder
  305. )
  306. {
  307. bool bResult = false;
  308. FolderPathInfo fpi;
  309. //
  310. // Length is "root" + '\' + "folder" + <nul>
  311. //
  312. fpi.cchPath = lstrlen(pszRoot) + lstrlen(pszFolder) + 2;
  313. fpi.pszPath = (LPTSTR)LocalAlloc(LPTR, MAX(fpi.cchPath, DWORD(MAX_PATH)) * sizeof(TCHAR));
  314. if (NULL != fpi.pszPath)
  315. {
  316. PathCombine(fpi.pszPath, pszRoot, pszFolder);
  317. if (PushFolderPathInfo(fpi))
  318. bResult = true;
  319. else
  320. LocalFree(fpi.pszPath);
  321. }
  322. return bResult;
  323. }
  324. //
  325. // Increases the size of the _pszPath buffer by a specified amount.
  326. // Original contents of buffer ARE NOT preserved.
  327. // Returns:
  328. // S_FALSE - _pszPath buffer was large enough. Not modified.
  329. // S_OK - _pszPath points to new bigger buffer.
  330. // E_OUTOFMEMORY - _pszPath points to original unmodified buffer.
  331. //
  332. HRESULT
  333. COfflineFilesEnum::GrowPathBuffer(
  334. INT cchRequired,
  335. INT cchExtra
  336. )
  337. {
  338. HRESULT hres = S_FALSE;
  339. if (_cchPathBuf <= cchRequired)
  340. {
  341. LPTSTR pszNewBuf = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cchRequired + cchExtra));
  342. if (NULL != pszNewBuf)
  343. {
  344. if (NULL != _pszPath)
  345. LocalFree(_pszPath);
  346. _pszPath = pszNewBuf;
  347. _cchPathBuf = cchRequired + cchExtra;
  348. hres = S_OK;
  349. }
  350. else
  351. {
  352. hres = E_OUTOFMEMORY; // Failure. Orig buffer is left intact.
  353. }
  354. }
  355. return hres;
  356. }
  357. //
  358. // Determine if user has access to view this file.
  359. //
  360. bool
  361. COfflineFilesEnum::UserHasAccess(
  362. const CscFindData& cscfd
  363. )
  364. {
  365. return _bUserIsAdmin ||
  366. CscAccessUser(cscfd.dwStatus) ||
  367. CscAccessGuest(cscfd.dwStatus);
  368. }
  369. //
  370. // Centralize any item-exclusion logic in a single function.
  371. //
  372. bool
  373. COfflineFilesEnum::Exclude(
  374. const CscFindData& cscfd
  375. )
  376. {
  377. return ((FILE_ATTRIBUTE_DIRECTORY & cscfd.fd.dwFileAttributes) ||
  378. (FLAG_CSC_COPY_STATUS_LOCALLY_DELETED & cscfd.dwStatus) ||
  379. ((FILE_ATTRIBUTE_HIDDEN & cscfd.fd.dwFileAttributes) && !_bShowHiddenFiles) ||
  380. (IsHiddenSystem(cscfd.fd.dwFileAttributes) && !_bShowSuperHiddenFiles) ||
  381. !UserHasAccess(cscfd));
  382. }
  383. //
  384. // If a folder is hidden and the current shell setting says to not show hidden files,
  385. // don't enumerate any children of a folder. Likewise for super hidden files and the
  386. // "show super hidden files" setting.
  387. //
  388. bool
  389. COfflineFilesEnum::OkToEnumFolder(
  390. const CscFindData& cscfd
  391. )
  392. {
  393. return (_bShowHiddenFiles || (0 == (FILE_ATTRIBUTE_HIDDEN & cscfd.fd.dwFileAttributes))) &&
  394. (_bShowSuperHiddenFiles || !IsHiddenSystem(cscfd.fd.dwFileAttributes));
  395. }
  396. HRESULT COfflineFilesEnum::Next(ULONG celt, LPITEMIDLIST *rgelt,
  397. ULONG *pceltFetched)
  398. {
  399. HRESULT hres;
  400. CscFindData cscfd;
  401. ULONG celtEnumed;
  402. //
  403. // If you've hit one of these asserts, you didn't call IsValid()
  404. // before using the enumerator.
  405. //
  406. TraceAssert(NULL != _pszPath);
  407. TraceAssert(NULL != _hdsaFolderPathInfo);
  408. //
  409. // This label is used to restart the enum if an item is excluded.
  410. //
  411. enum_start:
  412. hres = S_FALSE;
  413. celtEnumed = 0;
  414. ZeroMemory(&cscfd, sizeof(cscfd));
  415. if (!_hEnumShares.IsValid())
  416. {
  417. //
  418. // First time through.
  419. // Enumerate shares and files until we find a folder or file.
  420. //
  421. _hEnumShares = CacheFindFirst(NULL, &cscfd);
  422. if (_hEnumShares.IsValid())
  423. {
  424. _dwServerStatus = _ServerStatusCache.GetServerStatus(cscfd.fd.cFileName);
  425. do
  426. {
  427. //
  428. // Buffer attached to _pszPath is guaranteed to be at least
  429. // MAX_PATH so it's safe to copy cFileName[].
  430. //
  431. lstrcpy(_pszPath, cscfd.fd.cFileName);
  432. _hEnum = CacheFindFirst(_pszPath, &cscfd);
  433. if (_hEnum.IsValid())
  434. {
  435. celtEnumed = 1;
  436. }
  437. }
  438. while(0 == celtEnumed && CacheFindNext(_hEnumShares, &cscfd));
  439. }
  440. }
  441. else
  442. {
  443. if (_hEnum.IsValid())
  444. {
  445. if (CacheFindNext(_hEnum, &cscfd))
  446. {
  447. //
  448. // Most common case. Got next file in current folder.
  449. //
  450. celtEnumed = 1;
  451. }
  452. else
  453. {
  454. //
  455. // Enumeration exhausted for this folder. If we have folder paths
  456. // saved on the stack, keep popping them until we find one containing
  457. // at least one file or folder.
  458. //
  459. FolderPathInfo fpi;
  460. while(SUCCEEDED(hres) && 0 == celtEnumed && PopFolderPathInfo(&fpi) && NULL != fpi.pszPath)
  461. {
  462. _hEnum = CacheFindFirst(fpi.pszPath, &cscfd);
  463. if (_hEnum.IsValid())
  464. {
  465. //
  466. // The popped folder path is the only opportunity we have
  467. // where a string could overflow the temp _pszPath buffer.
  468. // If necesary, grow the buffer to hold the path. Add
  469. // room for an extra 100 chars to minimize re-growth.
  470. // Buffer is not altered if required path length is
  471. // less than _cchPathBuf.
  472. //
  473. if (FAILED(GrowPathBuffer(fpi.cchPath, 100)))
  474. hres = E_OUTOFMEMORY;
  475. if (SUCCEEDED(hres))
  476. {
  477. lstrcpy(_pszPath, fpi.pszPath);
  478. celtEnumed = 1;
  479. }
  480. }
  481. LocalFree(fpi.pszPath);
  482. }
  483. if (SUCCEEDED(hres))
  484. {
  485. while(0 == celtEnumed && CacheFindNext(_hEnumShares, &cscfd))
  486. {
  487. //
  488. // No more saved folder paths. This share is exhausted.
  489. // Enumerate next share. If next is empty, keep enumerating
  490. // shares until we find one with content. The buffer
  491. // attached to _pszPath is guaranteed to be at least MAX_PATH
  492. // so it's always safe to copy cFileName[].
  493. //
  494. _dwServerStatus = _ServerStatusCache.GetServerStatus(cscfd.fd.cFileName);
  495. lstrcpy(_pszPath, cscfd.fd.cFileName);
  496. _hEnum = CacheFindFirst(_pszPath, &cscfd);
  497. if (_hEnum.IsValid())
  498. {
  499. celtEnumed = 1;
  500. }
  501. }
  502. }
  503. }
  504. }
  505. }
  506. if (celtEnumed)
  507. {
  508. if (FILE_ATTRIBUTE_DIRECTORY & cscfd.fd.dwFileAttributes)
  509. {
  510. if (OkToEnumFolder(cscfd))
  511. {
  512. //
  513. // Save the folder path on a stack. This is how we enumerate
  514. // the cache item hierarcy as a flat list. We'll pop these off
  515. // the stack on future calls to Next() when all children of the
  516. // current folder have been enumerated.
  517. //
  518. if (!SaveFolderPath(_pszPath, cscfd.fd.cFileName))
  519. {
  520. //
  521. // Path not saved. Insufficient heap memory.
  522. // Abort the enumeration.
  523. //
  524. hres = E_OUTOFMEMORY;
  525. }
  526. }
  527. }
  528. if (SUCCEEDED(hres))
  529. {
  530. if (!Exclude(cscfd))
  531. {
  532. //
  533. // An IDList is composed of a fixed-length part and a variable-length
  534. // path+name buffer.
  535. // The path+name variable-length buffer is formatted as follows:
  536. //
  537. // dir1\dir2\dir3<nul>name<nul>
  538. //
  539. TCHAR szUNC[MAX_PATH];
  540. if (PathCombine(szUNC, _pszPath, cscfd.fd.cFileName))
  541. {
  542. hres = COfflineFilesFolder::OLID_CreateFromUNCPath(szUNC,
  543. &cscfd.fd,
  544. cscfd.dwStatus,
  545. cscfd.dwPinCount,
  546. cscfd.dwHintFlags,
  547. _dwServerStatus,
  548. (LPOLID *)&rgelt[0]);
  549. }
  550. }
  551. else
  552. {
  553. //
  554. // This item is excluded from the enumeration. Restart.
  555. // I normally don't like goto's but doing this with a loop
  556. // is just plain harder to understand. The goto is quite
  557. // appropriate in this circumstance.
  558. //
  559. goto enum_start;
  560. }
  561. }
  562. }
  563. if (pceltFetched)
  564. *pceltFetched = celtEnumed;
  565. return hres;
  566. }
  567. HRESULT COfflineFilesEnum::Skip(ULONG celt)
  568. {
  569. return E_NOTIMPL;
  570. }
  571. HRESULT COfflineFilesEnum::Reset()
  572. {
  573. _hEnum.Close();
  574. _hEnumShares.Close();
  575. return S_OK;
  576. }
  577. HRESULT COfflineFilesEnum::Clone(IEnumIDList **ppenum)
  578. {
  579. return E_NOTIMPL;
  580. }