windows-nt/Source/XPSP1/NT/com/ole32/cs/backend/csacc.cxx
2020-09-26 16:20:57 +08:00

787 lines
20 KiB
C++

//
// Author: DebiM
// Date: September 1996
//
// File: csacc.cxx
//
// Class Store Manager implementation for a client desktop.
//
// This source file contains implementations for IClassAccess
// interface for CClassAccess object.
// It also contains the IEnumPackage implementation for the
// aggregate of all class containers seen by the caller.
//
//
//---------------------------------------------------------------------
#include "cstore.hxx"
IClassAccess *GetNextValidClassStore(CLASSCONTAINER **pStoreList,
DWORD cStores,
PSID pUserSid,
uCLSSPEC* pClassSpec,
BOOL fCache,
DWORD* pcount);
extern HRESULT GetUserClassStores(
PCLASSCONTAINER **ppStoreList,
DWORD *pcStores,
BOOL *pfCache,
PSID *ppUserSid);
//
// Link list pointer for Class Containers Seen
//
extern CLASSCONTAINER *gpContainerHead;
//
// Link list pointer for User Profiles Seen
//
extern USERPROFILE *gpUserHead;
//
// Global Class Factory for Class Container
//
extern CAppContainerCF *pCF;
//
// Critical Section used during operations on list of class stores
//
extern CRITICAL_SECTION ClassStoreBindList;
//
// CClassAccess implementation
//
CClassAccess::CClassAccess()
{
m_uRefs = 1;
m_cCalls = 0;
pStoreList = NULL;
cStores = 0;
}
CClassAccess::~CClassAccess()
{
DWORD i;
for (i = 0; i < cStores; i++)
{
if (pStoreList[i]->gpClassStore)
{
(pStoreList[i]->gpClassStore)->Release();
pStoreList[i]->gpClassStore = NULL;
CSDBGPrint((L"Found open container and closed."));
}
if (pStoreList[i]->pszClassStorePath)
{
CoTaskMemFree (pStoreList[i]->pszClassStorePath);
pStoreList[i]->pszClassStorePath = NULL;
}
CoTaskMemFree(pStoreList[i]);
pStoreList[i] = NULL;
}
CoTaskMemFree(pStoreList);
cStores = NULL;
}
//----------------------------------------------------------------------
//
//
void PrintClassSpec(
uCLSSPEC * pclsspec // Class Spec (GUID/Ext/MIME)
)
{
STRINGGUID szClsid;
if (pclsspec->tyspec == TYSPEC_CLSID)
{
StringFromGUID (pclsspec->tagged_union.clsid, szClsid);
CSDBGPrint((L" ... GetAppInfo by CLSID = %s", szClsid));
}
if (pclsspec->tyspec == TYSPEC_PROGID)
{
CSDBGPrint((L" ... GetAppInfo by ProgID = %s",
pclsspec->tagged_union.pProgId));
}
if (pclsspec->tyspec == TYSPEC_MIMETYPE)
{
CSDBGPrint((L" ... GetAppInfo by MimeType = %s",
pclsspec->tagged_union.pMimeType));
}
if (pclsspec->tyspec == TYSPEC_FILEEXT)
{
CSDBGPrint((L" ... GetAppInfo by FileExt = %s",
pclsspec->tagged_union.pFileExt));
}
if (pclsspec->tyspec == TYSPEC_IID)
{
StringFromGUID (pclsspec->tagged_union.iid, szClsid);
CSDBGPrint((L" ... GetAppInfo by IID = %s", szClsid));
}
}
//----------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CClassAccess::GetAppInfo(
uCLSSPEC * pclsspec, // Class Spec (GUID/Ext/MIME)
QUERYCONTEXT * pQryContext, // Query Attributes
PACKAGEDISPINFO * pPackageInfo
)
//
// This is the most common method to access the Class Store.
// It queries the class store for implementations for a specific
// Class Id, or File Ext, or ProgID or MIME type.
//
// If a matching implementation is available for the object type,
// client architecture, locale and class context pointer to the
// binary is returned.
{
//
// Assume that this method is called in the security context
// of the user process. Hence there is no need to impersonate.
//
//
// Get the list of Class Stores for this user
//
HRESULT hr = S_OK;
ULONG i = 0, j = 0, k= 0;
IClassAccess * pICA = NULL;
BOOL fCache = FALSE;
PSID pUserSid = NULL;
BOOL fFound = FALSE;
QUERYCONTEXT QueryContext;
// added later.
if (gDebug)
{
WCHAR Name[32];
DWORD NameSize = 32;
if ( ! GetUserName( Name, &NameSize ) )
CSDBGPrint((L"GetAppInfo GetUserName failed 0x%x", GetLastError()));
else
CSDBGPrint((L"GetAppInfo as %s", Name));
}
if ((!pPackageInfo) ||
(!IsValidReadPtrIn(pPackageInfo, sizeof(PACKAGEDISPINFO))))
return E_INVALIDARG;
memset(pPackageInfo, 0, sizeof(PACKAGEDISPINFO));
if ( pQryContext )
{
QueryContext = *pQryContext;
}
else
{
// gets the default information.
QueryContext.dwContext = CLSCTX_ALL;
GetDefaultPlatform( &QueryContext.Platform );
QueryContext.Locale = MAKELCID(GetUserDefaultLangID(), SUBLANG_NEUTRAL);
QueryContext.dwVersionHi = (DWORD) -1;
QueryContext.dwVersionLo = (DWORD) -1;
}
if (gDebug)
PrintClassSpec(pclsspec);
if (!pStoreList)
hr = GetUserClassStores(
&pStoreList,
&cStores,
&fCache,
&pUserSid);
ERROR_ON_FAILURE(hr);
for (i=0; i < cStores; i++)
{
if (!(pICA = GetNextValidClassStore(pStoreList, cStores, pUserSid, pclsspec, fCache, &i)))
continue;
//
// Call method on this store
//
pICA->AddRef();
hr = pICA->GetAppInfo(
pclsspec,
&QueryContext,
pPackageInfo);
// Release it after use.
pICA->Release();
//
// Special case error return E_INVALIDARG
// Do not continue to look, return this.
//
if (hr == E_INVALIDARG)
{
ERROR_ON_FAILURE(hr);
}
//
// maintain access counters
//
(pStoreList[i])->cAccess++;
//
// We are iterating through the class stores from highest precedence to lowest --
// thus, the first container to return success will be our choice.
//
if (SUCCEEDED(hr))
{
fFound = TRUE;
break;
}
else
{
(pStoreList[i])->cNotFound++;
CSDBGPrint((L"CClassAccess::GetAppInfo() returned 0x%x", hr));
}
hr = S_OK;
}
Error_Cleanup:
if (pUserSid)
CoTaskMemFree (pUserSid);
if (fFound)
{
CSDBGPrint((L"Returning Package %s", pPackageInfo->pszPackageName));
return S_OK;
}
if (!SUCCEEDED(hr))
return hr;
return CS_E_PACKAGE_NOTFOUND;
}
#define MAX_GUID_CCH 38
//
// IsClassStoreForPolicy
//
BOOL IsClassStoreForPolicy(CLASSCONTAINER* pClassStore,
LPWSTR wszPolicyId)
{
LPWSTR pszPolicyGuid;
// Path looks like:
// LDAP://CN=<Class Store Name>,CN=<user-or-machine>,CN=<{policy-guid}>,...
//
// Look for ',' first
//
pszPolicyGuid = wcschr(pClassStore->pszClassStorePath, L',');
if (!pszPolicyGuid)
{
return FALSE;
}
//
// Look for the second ','
//
pszPolicyGuid = wcschr(pszPolicyGuid + 1, L',');
if (!pszPolicyGuid)
{
return FALSE;
}
//
// Now get to '{' at start of guid -- it is 4 chars
// past the ',' which we are currently at. Use wcschr
// to make sure we don't go past the end of the string
// and that our assumptions about the structure of the
// path are correct
//
if (wcschr(pszPolicyGuid, L'{') == (pszPolicyGuid + 4))
{
pszPolicyGuid += 4;
//
// Now that we have the '{', we are at the start of the guid
// and can compare with the requested policy id
//
if (_wcsnicmp(pszPolicyGuid, wszPolicyId, MAX_GUID_CCH) == 0)
{
return TRUE;
}
}
return FALSE;
}
//
// GetNextValidClassStore
//
//
IClassAccess *GetNextValidClassStore(CLASSCONTAINER** pStoreList,
DWORD cStores,
PSID pUserSid,
uCLSSPEC* pClassSpec,
BOOL fCache,
DWORD * pcount)
{
IClassAccess *pretICA = NULL;
BOOL bSpecificPolicy;
LPWSTR wszPolicyGuid;
HRESULT hr;
wszPolicyGuid = NULL;
hr = S_OK;
bSpecificPolicy = pClassSpec ? TYSPEC_PACKAGENAME == pClassSpec->tyspec : FALSE;
if (bSpecificPolicy)
{
hr = StringFromCLSID(pClassSpec->tagged_union.ByName.PolicyId, &wszPolicyGuid);
}
if (SUCCEEDED(hr))
{
for (pStoreList += (*pcount); (*pcount) < cStores; (*pcount)++, pStoreList++)
{
if ((*pStoreList)->gpClassStore != NULL)
{
break;
}
if (bSpecificPolicy &&
!IsClassStoreForPolicy(*pStoreList, wszPolicyGuid))
{
continue;
}
if (FALSE) // ((*pStoreList)->cBindFailures >= MAX_BIND_ATTEMPTS)
{
// Number of continuous failures have reached MAX_BIND_ATTEMPTS
// for this container.
// Will temporarily disable lookups in this container.
// Report it in EventLog once
//
if ((*pStoreList)->cBindFailures == MAX_BIND_ATTEMPTS)
{
//LogCsPathError((*pStoreList)->pszClassStorePath, hr);
(*pStoreList)->cBindFailures++;
}
continue;
}
else
{
CSDBGPrint((L"CS: .. Connecting to Store %d ... %s..",
(*pcount),
(*pStoreList)->pszClassStorePath));
//
// Bind to this Class Store
//
if ((wcsncmp ((*pStoreList)->pszClassStorePath, L"ADCS:", 5) == 0) ||
(wcsncmp ((*pStoreList)->pszClassStorePath, L"LDAP:", 5) == 0))
{
//
// If the Storename starts with ADCS or LDAP
// it is NTDS based implementation. Call directly.
//
IClassAccess *pCA = NULL;
LPOLESTR szClassStore = (*pStoreList)->pszClassStorePath;
// skipping ADCS:
if (wcsncmp ((*pStoreList)->pszClassStorePath, L"ADCS:", 5) == 0)
szClassStore += 5;
hr = pCF->CreateConnectedInstance(
szClassStore,
pUserSid,
fCache,
(void **)&pCA);
if (SUCCEEDED(hr))
{
EnterCriticalSection (&ClassStoreBindList);
if ((*pStoreList)->gpClassStore != NULL)
{
pCA->Release();
pCA = NULL;
}
else
{
(*pStoreList)->gpClassStore = pCA;
pCA = NULL;
}
LeaveCriticalSection (&ClassStoreBindList);
}
}
else
{
//
// Support for Third Party Pluggable
// Class Stores is not in Beta2.
//
ReportEventCS(hr = CS_E_INVALID_PATH, CS_E_INVALID_PATH, (*pStoreList)->pszClassStorePath);
}
if (SUCCEEDED(hr))
{
(*pStoreList)->cBindFailures = 0;
hr = S_OK;
break;
}
if (!SUCCEEDED(hr))
{
CSDBGPrint((L"Failed to connect to this store"));
if ((*pStoreList)->cBindFailures == 0)
{
// First failue or First failure after successful
// binding.
// Report it in EventLog
//
//LogCsPathError((*pStoreList)->pszClassStorePath, hr);
}
((*pStoreList)->cBindFailures) ++;
continue;
}
}
}
}
if (wszPolicyGuid)
{
CoTaskMemFree(wszPolicyGuid);
}
if ((*pcount) != cStores)
pretICA = (*pStoreList)->gpClassStore;
return pretICA;
}
HRESULT STDMETHODCALLTYPE CClassAccess::EnumPackages(
LPOLESTR pszPackageName,
GUID *pCategory,
ULONGLONG *pLastUsn,
DWORD dwAppFlags, // AppType options
IEnumPackage **ppIEnumPackage)
{
//
// Get the list of Class Stores for this user
//
HRESULT hr = S_OK;
ULONG i;
IEnumPackage *Enum[MAXCLASSSTORES];
ULONG cEnum = 0;
CMergedEnumPackage *EnumMerged = NULL;
IClassAccess *pICA = NULL;
ULONGLONG LastUsn, CopyLastUsn, *pCopyLastUsn;
BOOL fCache = FALSE;
PSID pUserSid = NULL;
// added later.
if (gDebug)
{
WCHAR Name[32];
DWORD NameSize = 32;
if ( ! GetUserName( Name, &NameSize ) )
CSDBGPrint((L"EnumPackage GetUserName failed 0x%x", GetLastError()));
else
CSDBGPrint((L"EnumPackage as %s", Name));
}
LastUsn = 0;
if (pLastUsn)
{
//
// Check pLastUsn
//
if (!IsValidReadPtrIn(pLastUsn, sizeof(ULONGLONG)))
return E_INVALIDARG;
pCopyLastUsn = &CopyLastUsn;
*pCopyLastUsn = *pLastUsn;
}
else
pCopyLastUsn = NULL;
//
// Get the list of Class Stores for this user
//
if (!pStoreList)
hr = GetUserClassStores(
&pStoreList,
&cStores,
&fCache,
&pUserSid);
*ppIEnumPackage = NULL;
if ((hr == S_OK) && (cStores == 0))
{
hr = CS_E_NO_CLASSSTORE;
}
if (!SUCCEEDED(hr))
{
//
// Free the Sid
//
if (pUserSid)
CoTaskMemFree (pUserSid);
return hr;
}
for (i=0; i < cStores; i++)
{
if (!(pICA = GetNextValidClassStore(pStoreList, cStores, pUserSid, NULL, fCache, &i)))
continue;
//
// Call method on this store
//
hr = pICA->EnumPackages (pszPackageName,
pCategory,
pCopyLastUsn,
dwAppFlags,
&(Enum[cEnum]));
if (hr == E_INVALIDARG)
{
break;
}
if (pCopyLastUsn)
{
if (LastUsn < *pCopyLastUsn)
LastUsn = *pCopyLastUsn;
*pCopyLastUsn = *pLastUsn;
}
if (SUCCEEDED(hr))
cEnum++;
}
if (SUCCEEDED(hr))
{
EnumMerged = new CMergedEnumPackage;
hr = EnumMerged->Initialize(Enum, cEnum);
if (FAILED(hr))
{
for (i = 0; i < cEnum; i++)
Enum[i]->Release();
delete EnumMerged;
}
else
{
hr = EnumMerged->QueryInterface(IID_IEnumPackage, (void **)ppIEnumPackage);
if (FAILED(hr))
delete EnumMerged;
}
if (pLastUsn)
{
if (LastUsn > *pLastUsn)
*pLastUsn = LastUsn;
}
}
if (pUserSid)
CoTaskMemFree (pUserSid);
return hr;
}
//--------------------------------------------------------------
CMergedEnumPackage::CMergedEnumPackage()
{
m_pcsEnum = NULL;
m_cEnum = 0;
m_csnum = 0;
m_dwRefCount = 0;
}
CMergedEnumPackage::~CMergedEnumPackage()
{
ULONG i;
for (i = 0; i < m_cEnum; i++)
m_pcsEnum[i]->Release();
CoTaskMemFree(m_pcsEnum);
}
HRESULT __stdcall CMergedEnumPackage::QueryInterface(REFIID riid,
void * * ppObject)
{
*ppObject = NULL; //gd
if ((riid==IID_IUnknown) || (riid==IID_IEnumPackage))
{
*ppObject=(IEnumPackage *) this;
}
else
{
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG __stdcall CMergedEnumPackage::AddRef()
{
InterlockedIncrement((long*) &m_dwRefCount);
return m_dwRefCount;
}
ULONG __stdcall CMergedEnumPackage::Release()
{
ULONG dwRefCount;
if ((dwRefCount = InterlockedDecrement((long*) &m_dwRefCount))==0)
{
delete this;
return 0;
}
return dwRefCount;
}
HRESULT __stdcall CMergedEnumPackage::Next(
ULONG celt,
PACKAGEDISPINFO *rgelt,
ULONG *pceltFetched)
{
ULONG count=0, total = 0;
HRESULT hr;
for (; m_csnum < m_cEnum; m_csnum++)
{
count = 0;
hr = m_pcsEnum[m_csnum]->Next(celt, rgelt+total, &count);
if (hr == E_INVALIDARG)
{
return hr;
}
total += count;
celt -= count;
if (!celt)
break;
}
if (pceltFetched)
*pceltFetched = total;
if (!celt)
return S_OK;
return S_FALSE;
}
HRESULT __stdcall CMergedEnumPackage::Skip(
ULONG celt)
{
PACKAGEDISPINFO *pPackageInfo = NULL;
HRESULT hr = S_OK;
ULONG cgot = 0, i;
pPackageInfo = (PACKAGEDISPINFO *)CoTaskMemAlloc(sizeof(PACKAGEDISPINFO)*celt);
if (!pPackageInfo)
return E_OUTOFMEMORY;
hr = Next(celt, pPackageInfo, &cgot);
for (i = 0; i < cgot; i++)
ReleasePackageInfo(pPackageInfo+i);
CoTaskMemFree(pPackageInfo);
return hr;
}
HRESULT __stdcall CMergedEnumPackage::Reset()
{
ULONG i;
for (i = 0; ((i <= m_csnum) && (i < m_cEnum)); i++)
m_pcsEnum[i]->Reset(); // ignoring all error values
m_csnum = 0;
return S_OK;
}
HRESULT __stdcall CMergedEnumPackage::Clone(IEnumPackage **ppIEnumPackage)
{
ULONG i;
CMergedEnumPackage *pClone;
IEnumPackage **pcsEnumCloned=NULL;
pClone = new CMergedEnumPackage;
pcsEnumCloned = (IEnumPackage **)CoTaskMemAlloc(sizeof(IEnumPackage *)*m_cEnum);
if (!pcsEnumCloned)
return E_OUTOFMEMORY;
for ( i = 0; i < m_cEnum; i++)
m_pcsEnum[i]->Clone(&(pcsEnumCloned[i]));
pClone->m_csnum = m_csnum;
pClone->Initialize(pcsEnumCloned, m_cEnum);
*ppIEnumPackage = (IEnumPackage *)pClone;
pClone->AddRef();
CoTaskMemFree(pcsEnumCloned);
return S_OK;
}
HRESULT CMergedEnumPackage::Initialize(IEnumPackage **pcsEnum, ULONG cEnum)
{
ULONG i;
m_csnum = 0;
m_pcsEnum = (IEnumPackage **)CoTaskMemAlloc(sizeof(IEnumPackage *) * cEnum);
if (!m_pcsEnum)
return E_OUTOFMEMORY;
for (i = 0; i < cEnum; i++)
m_pcsEnum[i] = pcsEnum[i];
m_cEnum = cEnum;
return S_OK;
}