//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: enum.cpp // //-------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include // QITAB, QISearch #include // ILFree(), etc #include "folder.h" #include "security.h" // // Create a single entry in the server status cache. // CServerStatusCache::CEntry::CEntry( LPCTSTR pszServer, DWORD dwStatus ) : m_pszServer(StrDup(pszServer)), m_dwStatus(dwStatus) { } // // Destroy a single entry in the server status cache. // CServerStatusCache::CEntry::~CEntry( void ) { if (NULL != m_pszServer) { LocalFree(m_pszServer); } } // // Destroy the server status cache. // CServerStatusCache::~CServerStatusCache( void ) { if (NULL != m_hdpa) { // // Delete each entry in the DPA then destroy the // DPA itself. // int cEntries = DPA_GetPtrCount(m_hdpa); for (int i = 0; i < cEntries; i++) { delete (CEntry *)DPA_GetPtr(m_hdpa, i); } DPA_Destroy(m_hdpa); } } // // Add a share's status to the cache. We strip the UNC path to it's // bare server name then add the status to the cache. If there's // no existing entry we just add it. If there is an existing entry, // we bitwise OR the status bits in with the existing entry. This way // the status of the server is the summation of the status of all // it's shares. // bool CServerStatusCache::AddShareStatus( LPCTSTR pszShare, DWORD dwShareStatus ) { bool bResult = true; TCHAR szServer[MAX_PATH]; CEntry *pEntry = FindEntry(ServerFromUNC(pszShare, szServer, ARRAYSIZE(szServer))); if (NULL != pEntry) { // // Found existing server entry for this share. Merge in the // status bits for this share. // pEntry->AddStatus(dwShareStatus); } else { // // No existing entry for this share's server. // if (NULL == m_hdpa) { // // No DPA exists yet. Create one. // We delay creation of the DPA until we really need one. // m_hdpa = DPA_Create(8); } if (NULL != m_hdpa) { // // We have a DPA. Create a new entry for this share's server // and add it to the DPA. // pEntry = new CEntry(szServer, dwShareStatus); if (NULL != pEntry) { if (!pEntry->IsValid() || -1 == DPA_AppendPtr(m_hdpa, pEntry)) { // // One of the following happened: // 1. Failure allocating server name in CEntry obj. // 2. Failure adding CEntry obj ptr to DPA. // delete pEntry; bResult = false; } } } else { bResult = false; // DPA creation failed. } } return bResult; } // // Obtain the CSC status bits for a given server. // This function assumes the pszUNC arg is a valid UNC path. // DWORD CServerStatusCache::GetServerStatus( LPCTSTR pszUNC ) { TCHAR szServer[MAX_PATH]; CEntry *pEntry = FindEntry(ServerFromUNC(pszUNC, szServer, ARRAYSIZE(szServer))); if (NULL == pEntry) { // // No entry for this server. Scan the CSC cache and pick up any new // servers added. Since the lifetime of this server cache is only for a single // enumeration, we should have to do this only once. However, if for some // reason, something gets added to the CSC cache while we're opening the viewer, // this code path will pick up the new server entry. // WIN32_FIND_DATA fd; DWORD dwStatus = 0; CCscFindHandle hFind = CacheFindFirst(NULL, &fd, &dwStatus, NULL, NULL, NULL); if (hFind.IsValid()) { do { AddShareStatus(fd.cFileName, dwStatus); } while(CacheFindNext(hFind, &fd, &dwStatus, NULL, NULL, NULL)); } // // Now that we have rescanned the CSC cache, try it again. // pEntry = FindEntry(szServer); } return pEntry ? pEntry->GetStatus() : 0; } // // Find a single entry in the server cache. // Assumes pszServer is a raw server name (not UNC). // Returns NULL if no match found. // CServerStatusCache::CEntry * CServerStatusCache::FindEntry( LPCTSTR pszServer ) { CEntry *pEntry = NULL; if (NULL != m_hdpa) { int cEntries = DPA_GetPtrCount(m_hdpa); for (int i = 0; i < cEntries; i++) { CEntry *pe = (CEntry *)DPA_GetPtr(m_hdpa, i); if (0 == lstrcmpi(pe->GetServer(), pszServer)) { pEntry = pe; break; } } } return pEntry; } LPTSTR CServerStatusCache::ServerFromUNC( LPCTSTR pszShare, LPTSTR pszServer, UINT cchServer ) { LPTSTR pszReturn = pszServer; // Remember for return. cchServer--; // Leave room for terminating nul. while(*pszShare && TEXT('\\') == *pszShare) pszShare++; while(*pszShare && TEXT('\\') != *pszShare && cchServer--) *pszServer++ = *pszShare++; *pszServer = TEXT('\0'); return pszReturn; } STDMETHODIMP COfflineFilesEnum::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(COfflineFilesEnum, IEnumIDList), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_ (ULONG) COfflineFilesEnum::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_ (ULONG) COfflineFilesEnum::Release() { if (InterlockedDecrement(&_cRef)) return _cRef; delete this; return 0; } COfflineFilesEnum::COfflineFilesEnum(DWORD grfFlags, COfflineFilesFolder *pfolder) { _cRef = 1; // // The minimum size of the buffer must be MAX_PATH. // The enumeration code is designed to grow it as needed. // _cchPathBuf = MAX_PATH; _pszPath = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * _cchPathBuf); if (NULL != _pszPath) *_pszPath = TEXT('\0'); else _cchPathBuf = 0; _grfFlags = grfFlags, _pfolder = pfolder; _pfolder->AddRef(); _dwServerStatus = 0; _hdsaFolderPathInfo = DSA_Create(sizeof(FolderPathInfo), 10); // // Determine if we should be showing system and/or hidden files. // _bShowHiddenFiles = boolify(ShowHidden()); _bShowSuperHiddenFiles = boolify(ShowSuperHidden()); _bUserIsAdmin = boolify(IsCurrentUserAnAdminMember()); DllAddRef(); } COfflineFilesEnum::~COfflineFilesEnum() { if (_pfolder) _pfolder->Release(); Reset(); if (_hdsaFolderPathInfo) { int cPaths = DSA_GetItemCount(_hdsaFolderPathInfo); FolderPathInfo fpi; for (int i = 0; i < cPaths; i++) { if (DSA_GetItem(_hdsaFolderPathInfo, i, &fpi) && NULL != fpi.pszPath) LocalFree(fpi.pszPath); } DSA_Destroy(_hdsaFolderPathInfo); } if (NULL != _pszPath) LocalFree(_pszPath); DllRelease(); } // // Since we're not throwing exceptions, clients must call this after ctor // to verify allocations succeeded. // bool COfflineFilesEnum::IsValid( void ) const { return (NULL != _hdsaFolderPathInfo) && (NULL != _pszPath); } bool COfflineFilesEnum::PopFolderPathInfo( FolderPathInfo *pfpi ) { bool bResult = false; TraceAssert(NULL != _hdsaFolderPathInfo); int iItem = DSA_GetItemCount(_hdsaFolderPathInfo) - 1; if ((0 <= iItem) && DSA_GetItem(_hdsaFolderPathInfo, iItem, pfpi)) { DSA_DeleteItem(_hdsaFolderPathInfo, iItem); bResult = true; } return bResult; } // // Build complete path to folder in a heap allocation and push it onto // stack of saved folder paths. // Returns false if memory can't be allocated for path. // bool COfflineFilesEnum::SaveFolderPath( LPCTSTR pszRoot, LPCTSTR pszFolder ) { bool bResult = false; FolderPathInfo fpi; // // Length is "root" + '\' + "folder" + // fpi.cchPath = lstrlen(pszRoot) + lstrlen(pszFolder) + 2; fpi.pszPath = (LPTSTR)LocalAlloc(LPTR, MAX(fpi.cchPath, DWORD(MAX_PATH)) * sizeof(TCHAR)); if (NULL != fpi.pszPath) { PathCombine(fpi.pszPath, pszRoot, pszFolder); if (PushFolderPathInfo(fpi)) bResult = true; else LocalFree(fpi.pszPath); } return bResult; } // // Increases the size of the _pszPath buffer by a specified amount. // Original contents of buffer ARE NOT preserved. // Returns: // S_FALSE - _pszPath buffer was large enough. Not modified. // S_OK - _pszPath points to new bigger buffer. // E_OUTOFMEMORY - _pszPath points to original unmodified buffer. // HRESULT COfflineFilesEnum::GrowPathBuffer( INT cchRequired, INT cchExtra ) { HRESULT hres = S_FALSE; if (_cchPathBuf <= cchRequired) { LPTSTR pszNewBuf = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cchRequired + cchExtra)); if (NULL != pszNewBuf) { if (NULL != _pszPath) LocalFree(_pszPath); _pszPath = pszNewBuf; _cchPathBuf = cchRequired + cchExtra; hres = S_OK; } else { hres = E_OUTOFMEMORY; // Failure. Orig buffer is left intact. } } return hres; } // // Determine if user has access to view this file. // bool COfflineFilesEnum::UserHasAccess( const CscFindData& cscfd ) { return _bUserIsAdmin || CscAccessUser(cscfd.dwStatus) || CscAccessGuest(cscfd.dwStatus); } // // Centralize any item-exclusion logic in a single function. // bool COfflineFilesEnum::Exclude( const CscFindData& cscfd ) { return ((FILE_ATTRIBUTE_DIRECTORY & cscfd.fd.dwFileAttributes) || (FLAG_CSC_COPY_STATUS_LOCALLY_DELETED & cscfd.dwStatus) || ((FILE_ATTRIBUTE_HIDDEN & cscfd.fd.dwFileAttributes) && !_bShowHiddenFiles) || (IsHiddenSystem(cscfd.fd.dwFileAttributes) && !_bShowSuperHiddenFiles) || !UserHasAccess(cscfd)); } // // If a folder is hidden and the current shell setting says to not show hidden files, // don't enumerate any children of a folder. Likewise for super hidden files and the // "show super hidden files" setting. // bool COfflineFilesEnum::OkToEnumFolder( const CscFindData& cscfd ) { return (_bShowHiddenFiles || (0 == (FILE_ATTRIBUTE_HIDDEN & cscfd.fd.dwFileAttributes))) && (_bShowSuperHiddenFiles || !IsHiddenSystem(cscfd.fd.dwFileAttributes)); } HRESULT COfflineFilesEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) { HRESULT hres; CscFindData cscfd; ULONG celtEnumed; // // If you've hit one of these asserts, you didn't call IsValid() // before using the enumerator. // TraceAssert(NULL != _pszPath); TraceAssert(NULL != _hdsaFolderPathInfo); // // This label is used to restart the enum if an item is excluded. // enum_start: hres = S_FALSE; celtEnumed = 0; ZeroMemory(&cscfd, sizeof(cscfd)); if (!_hEnumShares.IsValid()) { // // First time through. // Enumerate shares and files until we find a folder or file. // _hEnumShares = CacheFindFirst(NULL, &cscfd); if (_hEnumShares.IsValid()) { _dwServerStatus = _ServerStatusCache.GetServerStatus(cscfd.fd.cFileName); do { // // Buffer attached to _pszPath is guaranteed to be at least // MAX_PATH so it's safe to copy cFileName[]. // lstrcpy(_pszPath, cscfd.fd.cFileName); _hEnum = CacheFindFirst(_pszPath, &cscfd); if (_hEnum.IsValid()) { celtEnumed = 1; } } while(0 == celtEnumed && CacheFindNext(_hEnumShares, &cscfd)); } } else { if (_hEnum.IsValid()) { if (CacheFindNext(_hEnum, &cscfd)) { // // Most common case. Got next file in current folder. // celtEnumed = 1; } else { // // Enumeration exhausted for this folder. If we have folder paths // saved on the stack, keep popping them until we find one containing // at least one file or folder. // FolderPathInfo fpi; while(SUCCEEDED(hres) && 0 == celtEnumed && PopFolderPathInfo(&fpi) && NULL != fpi.pszPath) { _hEnum = CacheFindFirst(fpi.pszPath, &cscfd); if (_hEnum.IsValid()) { // // The popped folder path is the only opportunity we have // where a string could overflow the temp _pszPath buffer. // If necesary, grow the buffer to hold the path. Add // room for an extra 100 chars to minimize re-growth. // Buffer is not altered if required path length is // less than _cchPathBuf. // if (FAILED(GrowPathBuffer(fpi.cchPath, 100))) hres = E_OUTOFMEMORY; if (SUCCEEDED(hres)) { lstrcpy(_pszPath, fpi.pszPath); celtEnumed = 1; } } LocalFree(fpi.pszPath); } if (SUCCEEDED(hres)) { while(0 == celtEnumed && CacheFindNext(_hEnumShares, &cscfd)) { // // No more saved folder paths. This share is exhausted. // Enumerate next share. If next is empty, keep enumerating // shares until we find one with content. The buffer // attached to _pszPath is guaranteed to be at least MAX_PATH // so it's always safe to copy cFileName[]. // _dwServerStatus = _ServerStatusCache.GetServerStatus(cscfd.fd.cFileName); lstrcpy(_pszPath, cscfd.fd.cFileName); _hEnum = CacheFindFirst(_pszPath, &cscfd); if (_hEnum.IsValid()) { celtEnumed = 1; } } } } } } if (celtEnumed) { if (FILE_ATTRIBUTE_DIRECTORY & cscfd.fd.dwFileAttributes) { if (OkToEnumFolder(cscfd)) { // // Save the folder path on a stack. This is how we enumerate // the cache item hierarcy as a flat list. We'll pop these off // the stack on future calls to Next() when all children of the // current folder have been enumerated. // if (!SaveFolderPath(_pszPath, cscfd.fd.cFileName)) { // // Path not saved. Insufficient heap memory. // Abort the enumeration. // hres = E_OUTOFMEMORY; } } } if (SUCCEEDED(hres)) { if (!Exclude(cscfd)) { // // An IDList is composed of a fixed-length part and a variable-length // path+name buffer. // The path+name variable-length buffer is formatted as follows: // // dir1\dir2\dir3name // TCHAR szUNC[MAX_PATH]; if (PathCombine(szUNC, _pszPath, cscfd.fd.cFileName)) { hres = COfflineFilesFolder::OLID_CreateFromUNCPath(szUNC, &cscfd.fd, cscfd.dwStatus, cscfd.dwPinCount, cscfd.dwHintFlags, _dwServerStatus, (LPOLID *)&rgelt[0]); } } else { // // This item is excluded from the enumeration. Restart. // I normally don't like goto's but doing this with a loop // is just plain harder to understand. The goto is quite // appropriate in this circumstance. // goto enum_start; } } } if (pceltFetched) *pceltFetched = celtEnumed; return hres; } HRESULT COfflineFilesEnum::Skip(ULONG celt) { return E_NOTIMPL; } HRESULT COfflineFilesEnum::Reset() { _hEnum.Close(); _hEnumShares.Close(); return S_OK; } HRESULT COfflineFilesEnum::Clone(IEnumIDList **ppenum) { return E_NOTIMPL; }