|
|
/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
ASSOCQE.CPP
Abstract:
WinMgmt Association Query Engine
History:
raymcc 04-Jul-99 Adapted from QENGINE.CPP sources by revolutionary means, as it was 'Independence Day 1999'. raymcc 31-Jul-99 Finished classref support raymcc 19-Aug-99 Fixed security & IN/OUT tagging problems. raymcc 10-Sep-99 Remaining Win2K bugs raymcc 25-May-00 Assoc-by-rule
--*/
#include "precomp.h"
#include <stdio.h>
#include <stdlib.h>
#include <wbemcore.h>
#include <oahelp.inl>
#include <wqllex.h>
#include <wqlnode.h>
//
// parses a string like "REF:aaaa=cccc"
// returns "AAAA=CCCC" (it uses uppercase)
// it does not use more than MaxCch charactes from the dest buffer
// and it sets a null terminator there
//
/////////////////////////////////////////
void parse_REF(WCHAR * pSrc,size_t MaxCch,WCHAR * pOut) { if (L'R' == *pSrc || L'r' == *pSrc) pSrc++; else goto end_; if (L'E' == *pSrc || L'e' == *pSrc) pSrc++; else goto end_; if (L'F' == *pSrc || L'f' == *pSrc) pSrc++; else goto end_; if (L':' == *pSrc) pSrc++; else goto end_; WCHAR * pEnd = pOut + MaxCch - 1; while(*pSrc && (ULONG_PTR)pOut < (ULONG_PTR)pEnd) { *pOut = ToUpper((WCHAR)*pSrc); pOut++; pSrc++; } end_: *pOut = 0; }
#define WBEM_S_QUERY_OPTIMIZED_OUT 0x48001
//***************************************************************************
//
// Change these to ConfigMgr
//
//***************************************************************************
#define RUNAWAY_QUERY_TEST_THRESHOLD (60000*10)
#define START_ANOTHER_SINK_THRESHOLD (5000)
#define MAX_CONCURRENT_SINKS 5
#define MAX_CLASS_NAME 512 // SEC:REVIEWED
#define DYN_CLASS_CACHE_REUSE_WINDOW 5000
#define MAX_INTERLEAVED_RESOLUTIONS 5
//
//
// CAssocQuery::CAssocQuery
//
//***************************************************************************
// full profiler line coverage
CAssocQuery::CAssocQuery(): m_lRef(0), m_pDestSink(0), m_pEndpoint(0), m_bstrEndpointClass(0), m_bstrEndpointRelPath(0), m_bstrEndpointPath(0), m_bEndpointIsClass(false), m_dwQueryStartTime(0), m_dwLastResultTime(0), m_lActiveSinks(0), m_hSinkDoneEvent(0), m_pContext(0), m_pNs(0), m_bCancel(false), m_bLimitNeedsDecrement(false), m_Parser() { CAsyncServiceQueue* pTemp = ConfigMgr::GetAsyncSvcQueue(); if(pTemp) { pTemp->IncThreadLimit(); m_bLimitNeedsDecrement = true; pTemp->Release(); }
}
//
//
// CAssocQuery::~CAssocQuery()
//
//***************************************************************************
// full profiler line coverage
CAssocQuery::~CAssocQuery() { // Cleanup.
// ========
SysFreeString(m_bstrEndpointClass); SysFreeString(m_bstrEndpointRelPath); SysFreeString(m_bstrEndpointPath);
if (m_hSinkDoneEvent) CloseHandle(m_hSinkDoneEvent);
EmptyObjectList(m_aMaster); EmptyObjectList(m_aDynClasses);
// Release objects.
// ================
if (m_pDestSink) m_pDestSink->Release(); if (m_pEndpoint) m_pEndpoint->Release(); if (m_pContext) m_pContext->Release(); if (m_pNs) m_pNs->Release();
EmptyCandidateEpArray(); // Call this before deleting critsec
if(m_bLimitNeedsDecrement) { CAsyncServiceQueue* pTemp = ConfigMgr::GetAsyncSvcQueue(); if(pTemp) { pTemp->DecThreadLimit(); pTemp->Release(); } }
}
//
//
// CAssocQuery::CreateInst
//
// Mini factory
//
//***************************************************************************
// full profiler line coverage
CAssocQuery* CAssocQuery::CreateInst() { try { CAssocQuery *p = new CAssocQuery(); // CCritSec throws
if (p) p->AddRef(); return p; } catch (CX_Exception &) { return NULL; } }
//***************************************************************************
//
// CAssocQuery::AddRef
//
//***************************************************************************
// full profiler line coverage
ULONG CAssocQuery::AddRef() { return InterlockedIncrement(&m_lRef); }
//***************************************************************************
//
// CAssocQuery::Release
//
//***************************************************************************
// full profiler line coverage
ULONG CAssocQuery::Release() { long lRef = InterlockedDecrement(&m_lRef); if(lRef == 0) delete this; return lRef; }
//***************************************************************************
//
// CAssocQuery::QueryInterface
//
//***************************************************************************
// not called
HRESULT CAssocQuery::QueryInterface( REFIID riid, void** ppv ) { if (riid == IID_IUnknown) { *ppv = (IUnknown *) this; AddRef(); return S_OK; } return E_NOINTERFACE; }
//***************************************************************************
//
// CAssocQuery::Cancel
//
// Attempts to cancel the query in the prime of its life.
//
//***************************************************************************
// not called
HRESULT CAssocQuery::Cancel() { m_bCancel = true; return WBEM_S_NO_ERROR; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// BEGIN FLOW CONTROL
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//***************************************************************************
//
// CAssocQuery::Execute
//
// ENTRY POINT from QENGINE.CPP
//
// Attempts to executes a 'references' or 'associators' query.
// Returns status via <pSink>.
//
// Uses the calling thread to coordinate the entire query. The thread
// logically blocks (and does some background work) until the entire query
// is finished and is responsible for sending the final HRESULT
// to the destination sink.
//
// Ref count of 'this' is not changed in this function. On entry,
// the ref count is 1, so the caller makes the Release call.
//
//***************************************************************************
// ok
HRESULT CAssocQuery::Execute( IN CWbemNamespace *pNs, IN LPWSTR pszQuery, IN IWbemContext* pContext, IN CBasicObjectSink* pSink ) {
m_dwQueryStartTime = GetCurrentTime();
// Check the Repository.
// =====================
m_pNs = pNs; // Copy this for future use
m_pNs->AddRef();
HRESULT hRes = WBEM_E_FAILED; IWbemClassObject* pErrorObj = NULL; // keep the order of these two objects, since CSetStatusOnMe wants ErrObj to be alive
CReleaseMeRef<IWbemClassObject*> rmErr(pErrorObj); CSetStatusOnMe SetMe(pSink,hRes,pErrorObj);
// Parse the query.
hRes = m_Parser.Parse(pszQuery); if (FAILED(hRes)) return hRes;
// If the query is KEYSONLY, we can toss out the original
// context object and use a copy with merged in __GET_EXT_KEYS_ONLY
// techniques. Otherwise, we AddRef() the original context.
// =================================================================
BOOL bKeysOnlyQuery = (m_Parser.GetQueryType() & QUERY_TYPE_KEYSONLY) != 0; if (pContext) { if (bKeysOnlyQuery) { hRes = pContext->Clone(&m_pContext); if (FAILED(hRes)) return hRes;
hRes = m_pNs->MergeGetKeysCtx(m_pContext); if (FAILED(hRes)) return hRes; } else { m_pContext = pContext; // Yup, this too.
m_pContext->AddRef(); } }
// At this point, the query and object path are syntactically
// valid. That's all we know. Not much, eh?
//
// Next, get the endpoint referred to in the query.
// ===========================================================
hRes = pNs->Exec_GetObjectByPath((LPWSTR) m_Parser.GetTargetObjPath(), 0, pContext, &m_pEndpoint, &pErrorObj); if (FAILED(hRes)) return hRes;
rmErr.release(); pErrorObj = NULL;
// Record whether the endpoint is a class or instance.
CVARIANT v; m_pEndpoint->Get(L"__GENUS", 0, &v, 0, 0); if (v.GetLONG() == 1) m_bEndpointIsClass = true; else m_bEndpointIsClass = false;
// Initial validation.
// For SCHEMAONLY, the endpoint must be a class.
// For CLASSDEFS_ONLY, the endpoint must be an instance.
// Otherwise, the endpoint can be either a class or
// instance the association must be an instance.
// ====================================================
if (m_Parser.GetQueryType() & QUERY_TYPE_SCHEMA_ONLY) { if (m_bEndpointIsClass == false) return hRes = WBEM_E_INVALID_QUERY; } else if (m_Parser.GetQueryType() & QUERY_TYPE_CLASSDEFS_ONLY) { if (m_bEndpointIsClass == true) return hRes = WBEM_E_INVALID_QUERY;
// Don't allow CLASSDEFSONLY and RESULTCLASS at the same time.
if (m_Parser.GetResultClass() != 0) return hRes = WBEM_E_INVALID_QUERY; }
// Get the class hierarchy and other info about the endpoint.
// ==========================================================
hRes = St_GetObjectInfo(m_pEndpoint, &m_bstrEndpointClass, &m_bstrEndpointRelPath, &m_bstrEndpointPath, m_aEndpointHierarchy);
if (FAILED(hRes)) return hRes;
// Now we at least know if there is going to be a chance.
m_pDestSink = pSink; m_pDestSink->AddRef();
try { BranchToQueryType(); // Forward-only execution, conceptually
} catch(CX_Exception &) { return hRes = WBEM_E_CRITICAL_ERROR; }
SetMe.dismiss(); return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::BranchToQueryType
//
// This takes over once the query is known to be syntactically valid
// and the endpoint object was found.
//
// Status & results are returned to the destination sink in the
// deeper functions.
//
//***************************************************************************
// ok
void CAssocQuery::BranchToQueryType() { // Next, test for <SchemaOnly> or <ClassDefsOnly> query,
// which allows us a short-cut.
// =====================================================
if (m_Parser.GetQueryType() & QUERY_TYPE_SCHEMA_ONLY) { ExecSchemaQuery(); // forward-only branch
} // If here, we are executing a 'normal' query where
// the association must be an instance.
// ================================================
else { ExecNormalQuery(); } }
//****************************************************************************
//
// CAssocQuery::ExecSchemaQuery
//
// This executes a SCHEMAONLY.
//
// 1. Get the list of classes which can reference the endpoint.
// 2. If REFERENCES OF, branch.
// 3. IF ASSOCIATORS OF, branch.
//
// Execution model from this point:
// Deeper functions only Indicate() results or else return hRes to
// caller. The only SetStatus() call for the destination sink
// is at the bottom of this function.
//
//***************************************************************************
// ok
void CAssocQuery::ExecSchemaQuery() { HRESULT hRes; CFlexArray aResultSet;
// (1)
// ===
hRes = BuildMasterAssocClassList(aResultSet);
if (SUCCEEDED(hRes)) { // (2)
// ===
if (m_Parser.GetQueryType() & QUERY_TYPE_GETREFS) hRes = SchemaQ_RefsQuery(aResultSet); // (3)
// ===
else hRes = SchemaQ_AssocsQuery(aResultSet); }
m_pDestSink->Return(hRes); }
//****************************************************************************
//
// CAssocQuery::ExecNormalQuery
//
// This executes a normal query. The association object must be
// an instance pointing to the endpoint. Either endpoint can be a
// class or an instance.
//
//****************************************************************************
// ok
HRESULT CAssocQuery::ExecNormalQuery() { HRESULT hRes = WBEM_E_FAILED; IWbemClassObject * pErrObj = NULL; CSetStatusOnMe SetMe(m_pDestSink,hRes,pErrObj);
DWORD dwQueryType = m_Parser.GetQueryType();
// Set up some helper events.
// ==========================
m_hSinkDoneEvent = CreateEvent(0,0,0,0); if (NULL == m_hSinkDoneEvent) return hRes = WBEM_E_OUT_OF_MEMORY;
// Get the list of classes that can participate.
hRes = BuildMasterAssocClassList(m_aMaster); if (FAILED(hRes)) return hRes;
// Now reduce this to instantiable classes.
hRes = ReduceToRealClasses(m_aMaster); if (FAILED(hRes)) return hRes;
// Filter class list based on some quick analysis of the query.
hRes = NormalQ_PreQueryClassFilter(m_aMaster); if (FAILED(hRes)) return hRes;
// Remove non-dynamic classes, as we will get static refs all in one go.
// IMPORTANT: This must remain located after the zero-array size test above,
// because the array size *will* be zero if the relationships are
// all in the repository and we don't want the query to fail!
hRes = RemoveNonDynClasses(m_aMaster); if (FAILED(hRes)) return hRes;
if (ConfigMgr::ShutdownInProgress()) return hRes = WBEM_E_SHUTTING_DOWN;
// Now, we branch depending on the query type.
// REFERENCES OF
if (dwQueryType & QUERY_TYPE_GETREFS) { hRes = NormalQ_ReferencesOf(); } else // ASSOCIATORS OF
{ hRes = NormalQ_AssociatorsOf(); } if (FAILED(hRes)) return hRes;
// At this point, we simply wait until the
// total sink count is zero, indicating that the
// query is completed. We look at any errors
// that were reported and determine what to return.
while (m_lActiveSinks) { // Break if a sink finishes or 250 milliseconds pass
// =================================================
WaitForSingleObject(m_hSinkDoneEvent, 250);
// If doing an ASSOCIATORS OF query (not with CLASSDEFSONLY)
// then do some background tasking.
// =========================================================
if ((dwQueryType & QUERY_TYPE_GETASSOCS) != 0 && (dwQueryType & QUERY_TYPE_CLASSDEFS_ONLY) == 0) { hRes = ResolveEpPathsToObjects(MAX_INTERLEAVED_RESOLUTIONS); }
if (FAILED(hRes)) return hRes; if (m_bCancel) return hRes = WBEM_E_CALL_CANCELLED; }
// If an associators query, resolve the endpoints.
// ===============================================
if ((dwQueryType & QUERY_TYPE_GETASSOCS) != 0) { hRes = ResolveEpPathsToObjects(-1); } return hRes; }
//****************************************************************************
//
// CAssocQuery::LoadCheck
//
// Checks the load being induced by this query and prevents too much
// concurrency.
//
//****************************************************************************
// ok
HRESULT CAssocQuery::NormalQ_LoadCheck() { while (1) { if (m_lActiveSinks <= MAX_CONCURRENT_SINKS) break;
// If we have a lot of active sinks, see if they
// are fairly active, otherwise add another one.
// =============================================
DWORD dwNow = GetCurrentTime(); if (dwNow - m_dwLastResultTime > START_ANOTHER_SINK_THRESHOLD) break;
if (dwNow - m_dwQueryStartTime > RUNAWAY_QUERY_TEST_THRESHOLD) return WBEM_E_CRITICAL_ERROR;
Sleep(50); // Yield time to other threads
}
return WBEM_S_NO_ERROR; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// END FLOW CONTROL
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// BEGIN MASTER ASSOC CLASS LIST MANIPULATION
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//****************************************************************************
//
// CAssocQuery::BuildMasterAssocClassList
//
// This function determines all the classes that could reference the
// endpoint, depending on the query type.
//
// Note: If the endpoint is a class and the query type is NOT schema-only,
// this includes weakly typed classes that have HASCLASSREFS qualifiers
// which can actually potentially reference the endpoint.
//
// HRESULT only
// Does not access the destination sink on error.
//
// PARAMETERS:
// <aClasses> On entry, this is empty. On exit, it contains
// ref counted copies of cached classes. The objects
// within it need to be treated as read-only. If they
// are modified in any way, they should be cloned.
//
//****************************************************************************
// ok
HRESULT CAssocQuery::BuildMasterAssocClassList( IN OUT CFlexArray &aMaster ) { CWStringArray aAllRefClasses; HRESULT hRes;
BOOL bSchemaOnly = (m_Parser.GetQueryType() & QUERY_TYPE_SCHEMA_ONLY) != 0;
// If the endpoint is a class, we want to add in
// classes with HASCLASSREF qualifiers.
// =============================================
if (m_bEndpointIsClass && !bSchemaOnly) hRes = MergeInClassRefList(aMaster);
// Go to the repository and get all classes which
// can reference this class. Since a lot of duplicates
// can happen, we do a union of the class list as
// we move through it.
// ====================================================
for (int i = 0; i < m_aEndpointHierarchy.Size(); i++) { CWStringArray aRefClasses;
hRes = Db_GetRefClasses(m_aEndpointHierarchy[i],aRefClasses);
if (hRes == WBEM_E_NOT_FOUND) continue; // It might be a dynamic endpoint
else if (FAILED(hRes)) return hRes;
CWStringArray aTmp; CWStringArray::Union(aAllRefClasses, aRefClasses, aTmp); aAllRefClasses = aTmp; }
// Now get each class definition from the repository.
// This results in a lot of redundancy, since we end up
// with subclasses of classes which actually contain
// the references.
// ====================================================
for (i = 0; i < aAllRefClasses.Size(); i++) { LPWSTR pszClassName = aAllRefClasses[i];
IWbemClassObject *pObj = 0; hRes = Db_GetClass(pszClassName,&pObj); if (FAILED(hRes)) return hRes; CReleaseMe rmObj(pObj);
// See if the class can really reference the endpoint
// and discard it if not.
hRes = CanClassRefQueryEp(bSchemaOnly, pObj, 0); if (FAILED(hRes)) continue; if (CFlexArray::no_error == aMaster.Add(pObj)) { pObj->AddRef(); } }
// Now get the dynamic classes from class providers.
// =================================================
hRes = GetDynClasses();
// Eliminate all the classes that cannot really
// reference the endpoint.
// ============================================
for (i = 0; i < m_aDynClasses.Size(); i++) { IWbemClassObject *pDynClass = (IWbemClassObject *) m_aDynClasses[i]; hRes = CanClassRefQueryEp(bSchemaOnly, pDynClass, 0); if (FAILED(hRes)) continue;
// If here, we will keep the dyn class as a result
// set candidate.
if (CFlexArray::no_error == aMaster.Add(pDynClass)) { pDynClass->AddRef(); } }
#ifdef DIAGNOSTICS
ClassListDump(L"BuildMasterAssocClassList", aMaster); #endif
return WBEM_S_NO_ERROR; }
//****************************************************************************
//
// CAssocQuery::RemoveNonDynClasses
//
// Removes all classes which don't have [dynamic] qualifiers.
// This allows a single query to the repository for all references
// and individual queries to providers to be cleanly separated.
//
//****************************************************************************
// full profiler line coverage
HRESULT CAssocQuery::RemoveNonDynClasses( IN OUT CFlexArray &aMaster ) { HRESULT hRes1, hRes2;
for (int i = 0; i < aMaster.Size(); i++) { IWbemClassObject *pClass = (IWbemClassObject *) aMaster[i]; hRes1 = St_ObjHasQualifier(L"dynamic", pClass); hRes2 = St_ObjHasQualifier(L"rulebased", pClass);
if (FAILED(hRes1) && FAILED(hRes2)) { aMaster[i] = 0; pClass->Release(); } }
aMaster.Compress(); return WBEM_S_NO_ERROR; }
//****************************************************************************
//
// CAssocQuery::MergeInClassRefList
//
// Builds the list of classes from all sources which have HasClassRefs
// qualifiers. In addition, the class must be capable of referencing
// the endpoint when it is a class.
//
// Precondition: Query endpoint is known to be a class.
//
//****************************************************************************
// ok
HRESULT CAssocQuery::MergeInClassRefList( IN OUT CFlexArray &aResultSet ) { HRESULT hRes;
CFlexArray aTemp; hRes = Db_GetClassRefClasses(aTemp); if (FAILED(hRes)) return hRes;
int i; for (i = 0; i < aTemp.Size() && SUCCEEDED(hRes); i++) { IWbemClassObject *pClass = (IWbemClassObject *) aTemp[i]; HRESULT hResInner = CanClassRefQueryEp(FALSE, pClass, 0);
if (SUCCEEDED(hResInner)) { if (CFlexArray::no_error != aResultSet.Add(pClass)) { pClass->Release(); hRes = WBEM_E_OUT_OF_MEMORY; } } else pClass->Release(); }
// final cleanup, start from where the other loop ended
for (int j=i;j<aTemp.Size();j++) { IWbemClassObject *pClass = (IWbemClassObject *) aTemp[i]; pClass->Release(); }
return hRes; }
//****************************************************************************
//
// CAssocQuery::CanClassRefQueryEp
//
// Determines if a class can reference the endpoint class.
//
// This works for both strongly typed and CLASSREF typed objects.
//
// PARAMETERS:
// <bStrict> If TRUE, the match must be exact. The tested
// class must have properties which directly reference
// the endpoint class name. If FALSE, the class
// can have properties which reference any of the
// superclasses of the query endpoint class.
// <pCls> The class to test.
// <paNames> The role properties which would reference the query
// endpoint. (optional). If not NULL, should point to
// an empty array.
//
// Returns:
// WBEM_S_NO_ERROR if so
// WBEM_E_FAILED
//
//****************************************************************************
// partly tested
HRESULT CAssocQuery::CanClassRefQueryEp( IN BOOL bStrict, IN IWbemClassObject *pCls, OUT CWStringArray *paNames ) { BOOL bIsACandidate = FALSE; HRESULT hRes; CIMTYPE cType; LONG lFlavor;
LPCWSTR pszRole = m_Parser.GetRole();
// Loop through the properties trying to find a legitimate
// reference to our endpoint class.
// =======================================================
pCls->BeginEnumeration(WBEM_FLAG_REFS_ONLY);
while (1) { BSTR strPropName = 0; hRes = pCls->Next( 0, // Flags
&strPropName, // Name
0, // Value
&cType, // CIMTYPE
&lFlavor // FLAVOR
);
CSysFreeMe _1(strPropName);
if (hRes == WBEM_S_NO_MORE_DATA) break;
// If the ROLE property is specified, and this property is not that
// ROLE, we can immediately eliminate it.
// ================================================================
if (pszRole && wbem_wcsicmp(strPropName, pszRole) != 0) continue;
// Mask out references inherited from parent classes, if strict
// rules in force.
// ============================================================
//if (bStrict && lFlavor == WBEM_FLAVOR_ORIGIN_PROPAGATED)
// continue;
// If the object has reference properties which are not inherited
// from the parent, then it is immediately candidate.
// ===============================================================
hRes = CanPropRefQueryEp(bStrict, strPropName, pCls, 0); if (SUCCEEDED(hRes)) { bIsACandidate = TRUE; if (paNames) paNames->Add(strPropName); } } // Enum of ref properties
pCls->EndEnumeration();
if (bIsACandidate) return WBEM_S_NO_ERROR;
return WBEM_E_FAILED; }
//****************************************************************************
//
// CAssocQuery::GetCimTypeForRef
//
//****************************************************************************
//
HRESULT CAssocQuery::GetCimTypeForRef( IN IWbemClassObject *pCandidate, IN BSTR pszRole, OUT BSTR *strCimType ) { if (strCimType == 0) return WBEM_E_INVALID_PARAMETER; *strCimType = 0;
// Get the qualifier set for the specified <role> property.
// ========================================================
IWbemQualifierSet *pQSet = 0; HRESULT hRes = pCandidate->GetPropertyQualifierSet(pszRole, &pQSet); if (FAILED(hRes)) return WBEM_E_NOT_FOUND; CReleaseMe _1(pQSet);
// Now, get the type of the role.
// ==============================
CVARIANT vCimType; hRes = pQSet->Get(L"CIMTYPE", 0, &vCimType, 0); if (FAILED(hRes) || V_VT(&vCimType) != VT_BSTR) return WBEM_E_FAILED;
// Get the class name from it.
// ===========================
BSTR strRefClass = V_BSTR(&vCimType); if (wcslen_max(strRefClass,MAX_CLASS_NAME) > MAX_CLASS_NAME) return WBEM_E_FAILED;
wchar_t ClassName[MAX_CLASS_NAME]; *ClassName = 0; if (strRefClass) { if (wcslen_max(strRefClass,MAX_CLASS_NAME) > MAX_CLASS_NAME) return WBEM_E_FAILED; parse_REF(strRefClass,MAX_CLASS_NAME,ClassName); }
if (0 != ClassName[0]) { *strCimType = SysAllocString(ClassName); return WBEM_S_NO_ERROR; }
return WBEM_E_NOT_FOUND; }
//****************************************************************************
//
// CAssocQuery::DoesAssocInstRefQueryEp
//
// Determines if an association instance actually references the
// query endpoint. Returns the role via which it actually references
// the query endpoint.
//
//****************************************************************************
//
HRESULT CAssocQuery::DoesAssocInstRefQueryEp( IN IWbemClassObject *pObj, OUT BSTR *pstrRole ) { if (pstrRole == 0 || pObj == 0) return WBEM_E_INVALID_PARAMETER;
BOOL bIsACandidate = FALSE; HRESULT hRes;
// Loop through the properties trying to find a legitimate
// reference to our endpoint class.
// =======================================================
pObj->BeginEnumeration(WBEM_FLAG_REFS_ONLY);
while (1) { BSTR strPropName = 0; hRes = pObj->Next( 0, // Flags
&strPropName, // Name
0, // Value
0, 0 );
CSysFreeMe _1(strPropName);
if (hRes == WBEM_S_NO_MORE_DATA) break;
hRes = RoleTest(m_pEndpoint, pObj, m_pNs, strPropName, ROLETEST_MODE_PATH_VALUE); if (SUCCEEDED(hRes)) { *pstrRole = SysAllocString(strPropName); pObj->EndEnumeration(); return WBEM_S_NO_ERROR; } } // Enum of ref properties
pObj->EndEnumeration();
return WBEM_E_NOT_FOUND; }
//****************************************************************************
//
// CAssocQuery::NormalQ_PreQueryClassFilter
//
// For normal queries, filters the master class list depending on the
// query parameters and the query type to eliminate as many association
// classes as possible from participating in the query. This is done
// entirely by schema-level analysis and the query parameters.
//
// Also, if the query endpoint is a class, then we eliminate dynamic
// classes which don't have HasClassRefs qualifiers.
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::NormalQ_PreQueryClassFilter( CFlexArray &aMaster ) { HRESULT hRes; BOOL bChg = FALSE;
CWStringArray aResClassHierarchy; // Result class hierarchy
CWStringArray aAssocClassHierarchy; // Association class hierarchy
IWbemClassObject *pResClass = 0; // Result class object
IWbemClassObject *pAssocClass = 0; // Assoc class object
LPCWSTR pszResultClass = m_Parser.GetResultClass(); LPCWSTR pszAssocClass = m_Parser.GetAssocClass();
// Get the RESULTCLASS.
// ====================
if (pszResultClass) { HRESULT hRes = GetClassFromAnywhere(pszResultClass, 0, &pResClass); if (hRes == WBEM_E_NOT_FOUND) { EmptyObjectList(aMaster); return WBEM_S_NO_ERROR; } else if (FAILED(hRes)) return WBEM_E_FAILED;
// Get its hierarchy.
// ==================
hRes = St_GetObjectInfo( pResClass, 0, 0, 0, aResClassHierarchy );
if (FAILED(hRes)) return WBEM_E_FAILED;
// Get all the subclasses.
// =======================
CFlexArray aFamily; hRes = GetClassDynasty(pszResultClass, aFamily); OnDelete<CFlexArray &,void(*)(CFlexArray &),EmptyObjectList> ArrRelMe(aFamily);
for (int i = 0; i < aFamily.Size(); i++) { CVARIANT vClass; IWbemClassObject *pCls = (IWbemClassObject *) aFamily[i]; if (FAILED(hRes = pCls->Get(L"__CLASS", 0, &vClass, 0, 0))) return hRes; if (CFlexArray::no_error != aResClassHierarchy.Add(vClass.GetStr())) { return WBEM_E_OUT_OF_MEMORY; } } }
CReleaseMe _1(pResClass);
// If the ASSOCCLASS was specified, get it and its hierarchy.
// ==========================================================
if (pszAssocClass) { HRESULT hRes = GetClassFromAnywhere(pszAssocClass, 0, &pAssocClass); if (hRes == WBEM_E_NOT_FOUND) { EmptyObjectList(aMaster); return WBEM_S_NO_ERROR; } else if (FAILED(hRes)) return WBEM_E_FAILED;
// Get its hierarchy.
// ==================
hRes = St_GetObjectInfo( pAssocClass, 0, 0, 0, aAssocClassHierarchy );
if (FAILED(hRes)) return WBEM_E_FAILED; }
CReleaseMe _2(pAssocClass);
// Prepurge if REFERENCES OF + RESULTCLASS is used
// or ASSOCIATORS OR + ASSOCCLASS. In both of these cases, the master class
// list is largely irrelevant and will be mostly purged because these are present
// in the query.
//
// [a] If RESULTCLASS/ASSOCCLASS is directly mentioned in the master, the master is
// purged and RESULTCLASS/ASSOCCLASS is added.
//
// [b] If RESULTCLASS/ASSOCCLASS is a subclass of a class in the master list, we examine
// its class hierarchy and determine if any of its superclasses appear in
// the master list. If so, we purge the master list and replace it with a
// single entry, containing the RESULTCLASS def.
//
// [c] If RESULTCLASS/ASSOCCLASS is a superclass, we examine each class C in the
// master and determine if any of the superclasses of C are the
// RESULTCLASS/ASSOCCLASS. If so, we retain C in the master. If not, we purge it.
//
LPCWSTR pszTestClass = 0; // RESULTCLASS/ASSOCCLASS alias
IWbemClassObject *pTestClass = 0; CWStringArray *paTest = 0;
if ((m_Parser.GetQueryType() & QUERY_TYPE_GETREFS) && pszResultClass) { pszTestClass = pszResultClass; pTestClass = pResClass; paTest = &aResClassHierarchy; }
if ((m_Parser.GetQueryType() & QUERY_TYPE_GETASSOCS) && pszAssocClass) { pszTestClass = pszAssocClass; pTestClass = pAssocClass; paTest = &aAssocClassHierarchy; }
if (pszTestClass && pTestClass && paTest) { // Test [a] : Look for direct match.
// =================================
BOOL bPurgeAndReplace = FALSE;
for (int i = 0; i < aMaster.Size(); i++) { IWbemClassObject *pClass = (IWbemClassObject *) aMaster[i]; CVARIANT v; hRes = pClass->Get(L"__CLASS", 0, &v, 0, 0); if (FAILED(hRes)) return hRes;
if (wbem_wcsicmp(V_BSTR(&v), pszTestClass) == 0) { bPurgeAndReplace = TRUE; }
// Test [b]
// If here, there was no equivalence. So, the test class may be a subclass
// of a class in master. We simply look to see if this class name appears
// in the hierarchy of the result class.
// ===========================================================================
if (!bPurgeAndReplace) for (int ii = 0; ii < paTest->Size(); ii++) { if (wbem_wcsicmp(V_BSTR(&v), paTest->operator[](ii)) == 0) { bPurgeAndReplace = TRUE; break; } }
if (bPurgeAndReplace) { // Get rid of everything but this one.
// ===================================
EmptyObjectList(aMaster); // Will Release <pClass> once
if (CFlexArray::no_error == aMaster.Add(pTestClass)) { pTestClass->AddRef(); } break; } } }
// Process possibly-altered master class list using other filters.
// ===============================================================
for (int i = 0; i < aMaster.Size(); i++) { IWbemClassObject *pClass = (IWbemClassObject *) aMaster[i]; BOOL bKeep = TRUE;
CVARIANT v; hRes = pClass->Get(L"__CLASS", 0, &v, 0, 0); if (FAILED(hRes)) return hRes;
// If query type is REFERENCES OF
// ==============================
if (m_Parser.GetQueryType() & QUERY_TYPE_GETREFS) { // ROLE test
// =========
LPCWSTR pszRole = m_Parser.GetRole(); if (pszRole) { CWStringArray aNames; hRes = CanClassRefQueryEp(FALSE, pClass, &aNames); if (FAILED(hRes)) bKeep = FALSE; else { for (int ii = 0; ii < aNames.Size(); ii++) { if (wbem_wcsicmp(aNames[ii], pszRole) != 0) bKeep = FALSE; } } }
// REQUIREDQUALIFIER test
// =======================
LPCWSTR pszRequiredQual = m_Parser.GetRequiredQual(); if (pszRequiredQual) { hRes = St_ObjHasQualifier(pszRequiredQual, pClass); if (FAILED(hRes)) { // If not in the primary object, check subclasses.
CFlexArray aDynasty; hRes = GetClassDynasty(v.GetStr(), aDynasty); if (FAILED(hRes)) bKeep = FALSE;
int nCandidateCount = 0; for (int ii = 0; ii< aDynasty.Size(); ii++) { IWbemClassObject *pTestCls = (IWbemClassObject *) aDynasty[ii]; hRes = St_ObjHasQualifier(pszRequiredQual, pTestCls); if (SUCCEEDED(hRes)) nCandidateCount++; } EmptyObjectList(aDynasty); if (nCandidateCount == 0) bKeep = FALSE; // Nobody in the family has the qualifier
} }
// RESULTCLASS test, test [c]
// ==========================
LPCWSTR pszResultClass2 = m_Parser.GetResultClass(); if (pszResultClass2) { hRes = St_ObjIsOfClass(pszResultClass2, pClass); if (FAILED(hRes)) bKeep = FALSE; } }
// If query type is ASSOCIATORS OF
// ===============================
else { // ROLE test
// =========
LPCWSTR pszRole = m_Parser.GetRole(); if (pszRole) { CWStringArray aNames; hRes = CanClassRefQueryEp(FALSE, pClass, &aNames); if (FAILED(hRes)) bKeep = FALSE; else { bKeep = FALSE; for (int ii = 0; ii < aNames.Size(); ii++) { if (wbem_wcsicmp(aNames[ii], pszRole) == 0) bKeep = TRUE; } } }
// ASSOCCLASS, test[c]
// ===================
LPCWSTR pszAssocClass2 = m_Parser.GetAssocClass(); if (pszAssocClass2) { hRes = St_ObjIsOfClass(pszAssocClass2, pClass); if (FAILED(hRes)) bKeep = FALSE; }
// REQUIREDASSOCQUALIFER
// =====================
LPCWSTR pszRequiredAssocQual = m_Parser.GetRequiredAssocQual(); if (pszRequiredAssocQual) { hRes = St_ObjHasQualifier(pszRequiredAssocQual, pClass); if (FAILED(hRes)) { // If not in the primary object, check subclasses.
CFlexArray aDynasty; hRes = GetClassDynasty(v.GetStr(), aDynasty); if (FAILED(hRes)) bKeep = FALSE;
int nCandidateCount = 0; for (int ii = 0; ii < aDynasty.Size(); ii++) { IWbemClassObject *pTestCls = (IWbemClassObject *) aDynasty[ii]; hRes = St_ObjHasQualifier(pszRequiredAssocQual, pTestCls); if (SUCCEEDED(hRes)) nCandidateCount++; } EmptyObjectList(aDynasty); if (nCandidateCount == 0) bKeep = FALSE; // Nobody in the family has the qualifier
} }
// If RESULTCLASS was used, branch out and see if the association
// class can even reference it.
// ==============================================================
LPCWSTR pszResultClass3 = m_Parser.GetResultClass(); if (pszResultClass3 && m_bEndpointIsClass == FALSE) { // The above compound test is to err on the side of safety,
// as the following function cannot deal with CLASSREFs. So,
// we simply don't try to prefilter in that case.
// =========================================================
hRes = CanAssocClassRefUnkEp(pClass, aResClassHierarchy); if (FAILED(hRes)) bKeep = FALSE; }
// If RESULTROLE is used, ensure the class even has a property of this name.
// =========================================================================
LPCWSTR pszResultRole = m_Parser.GetResultRole(); if (pszResultRole) { CVARIANT v2; hRes = pClass->Get(pszResultRole, 0, &v2, 0, 0); if (FAILED(hRes)) bKeep = FALSE; }
} // end ASSOCIATORS OF test block
// If query endpoint is a class, eliminate [dynamic] classes which don't
// have HasClassRefs.
// ======================================================================
if (m_bEndpointIsClass) { hRes = St_ObjHasQualifier(L"dynamic", pClass); if (SUCCEEDED(hRes)) { hRes = St_ObjHasQualifier(L"HasClassRefs", pClass); if (FAILED(hRes)) bKeep = FALSE; } }
// Yawn. So what did we end up deciding, anyway?
// ==============================================
if (bKeep == FALSE) { aMaster[i] = 0; pClass->Release(); } }
// No Swiss Cheese allowed. Close them holes.
// ==========================================
aMaster.Compress();
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::CanAssocClassRefUnkEp
//
// Determines if the association class can reference the specified
// class.
//
// Returns:
// WBEM_S_NO_ERROR if the assoc can reference the specified class.
// WBEM_E_NOT_FOUND if the assoc cannot reference the class.
// WBEM_E_FAILED in other cases.
//
//***************************************************************************
//
HRESULT CAssocQuery::CanAssocClassRefUnkEp( IN IWbemClassObject *pAssocClass, IN CWStringArray &aUnkEpHierarchy ) { HRESULT hRes; BOOL bFound = FALSE;
// Loop through all references and see if any of them can
// reference any of the classes in the result class hierarchy.
// ===========================================================
hRes = pAssocClass->BeginEnumeration(WBEM_FLAG_REFS_ONLY);
while (1) { BSTR strPropName = 0;
hRes = pAssocClass->Next( 0, // Flags
&strPropName, // Name
0, // Value
0, 0 );
CSysFreeMe _1(strPropName);
if (hRes == WBEM_S_NO_MORE_DATA) break;
BSTR strCimType = 0; hRes = GetCimTypeForRef(pAssocClass, strPropName, &strCimType); CSysFreeMe _2(strCimType);
if (SUCCEEDED(hRes) && strCimType) for (int i = 0; i < aUnkEpHierarchy.Size(); i++) { if (wbem_wcsicmp(aUnkEpHierarchy[i], strCimType) == 0) { bFound = TRUE; break; } } }
pAssocClass->EndEnumeration();
if (bFound) { return WBEM_S_NO_ERROR; }
return WBEM_E_NOT_FOUND; }
//***************************************************************************
//
// CAssocQuery::ReduceToRealClasses
//
// Reduces the master class list to classes which can be instantiated.
//
// To have an instance, a class must
// 1. Have a [key] or be singleton
// 2. Not be abstract
// 3. Not have an instantiable superclass
//
// Parameters:
// IN OUT aMaster Contains the unpruned result inbound
// and contains the pruned result set outbound
//
// Return value:
// HRESULT The destination sink is not accessed
//
//***************************************************************************
//
HRESULT CAssocQuery::ReduceToRealClasses( IN OUT CFlexArray & aMaster ) { HRESULT hRes;
for (int i = 0; i < aMaster.Size(); i++) { BOOL bKeep = TRUE; IWbemClassObject *pObj = (IWbemClassObject *) aMaster[i];
// See if class is abstract.
// =========================
IWbemQualifierSet *pQSet = 0; hRes = pObj->GetQualifierSet(&pQSet); if (FAILED(hRes)) return hRes; CReleaseMe _1(pQSet);
CVARIANT v1, v2, v3; HRESULT hResAbstract = pQSet->Get(L"ABSTRACT", 0, &v1, 0); HRESULT hResSingleton = pQSet->Get(L"SINGLETON", 0, &v2, 0); HRESULT hResDynamic = pQSet->Get(L"DYNAMIC", 0, &v3, 0);
// See if there is at least one key.
// =================================
HRESULT hResHasKeys = WBEM_E_FAILED; pObj->BeginEnumeration(WBEM_FLAG_KEYS_ONLY); int nCount = 0;
while (1) { // Actually, we don't care about anything
// other than if keys even exist. We
// do this by simply testing how many
// times this iterates.
hRes = pObj->Next(0,0,0,0,0); if (hRes == WBEM_S_NO_MORE_DATA) break; nCount++; }
pObj->EndEnumeration(); if (nCount) hResHasKeys = WBEM_S_NO_ERROR;
// Decision matrix which perform tests as to whether
// this is an instantiable class.
// ==================================================
if (SUCCEEDED(hResAbstract)) // Abstracts are never instantiable
bKeep = FALSE; else if (SUCCEEDED(hResDynamic)) // Dynamics must be instantiable
bKeep = TRUE; else if (SUCCEEDED(hResHasKeys)) // Must be static/non-abstract
bKeep = TRUE; else if (SUCCEEDED(hResSingleton)) // Must be static/non-abstract
bKeep = TRUE; else bKeep = FALSE; // Must be plain old keyless class
// Final decision to zap or keep.
// ==============================
if (!bKeep) { aMaster[i] = 0; pObj->Release(); } }
aMaster.Compress();
// Next, eliminate subclass/superclass pairs.
// ==========================================
for (i = 0; i < aMaster.Size(); i++) { IWbemClassObject *pObj = (IWbemClassObject *) aMaster[i]; CWStringArray aHierarchy; hRes = St_GetObjectInfo(pObj, 0, 0, 0, aHierarchy); BOOL bKillIt = FALSE;
if (FAILED(hRes)) return WBEM_E_FAILED;
// We now have the class and all of its superclasses in
// <aHierarchy>. We need to look at all the other classes
// and see if any of them have a class name mentioned in
// this array.
// ========================================================
for (int i2 = 0; i2 < aMaster.Size(); i2++) { IWbemClassObject *pTest = (IWbemClassObject *) aMaster[i2];
if (pTest == 0 || i2 == i) continue; // If the object has already been eliminated or
// if we are comparing an object with itself
CVARIANT v; hRes = pTest->Get(L"__CLASS", 0, &v, 0, 0); if (FAILED(hRes)) return hRes;
LPWSTR pszName = V_BSTR(&v); if (pszName == 0) return WBEM_E_FAILED;
bKillIt = FALSE; for (int i3 = 0; i3 < aHierarchy.Size(); i3++) { if (wbem_wcsicmp(aHierarchy[i3], pszName) == 0) { bKillIt = TRUE; break; } } if (bKillIt) break; }
if (bKillIt) { aMaster[i] = 0; pObj->Release(); } }
// Get rid of NULL entries.
// ========================
aMaster.Compress();
#ifdef DIAGNOSTICS
ClassListDump(L"Reduced Class Set", aMaster); #endif
return WBEM_S_NO_ERROR; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// END MASTER ASSOC CLASS LIST MANIPULATION
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// BEGIN NORMAL QUERY SUPPORT
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//****************************************************************************
//
// CAssocQuery::NormalQ_ReferencesOf
//
// Entry point for all normal REFERENCES OF queries.
//
//****************************************************************************
// untested; no support for classrefs, autoassocs, or classdefsonly
HRESULT CAssocQuery::NormalQ_ReferencesOf() { HRESULT hRes;
// Issue one-time call into repository for static instances.
CObjectSink * pSink = CreateSink(FilterForwarder_NormalRefs, L"<objdb refs request>"); if (NULL == pSink) return WBEM_E_OUT_OF_MEMORY; CReleaseMe rmSink(pSink);
hRes = Db_GetInstRefs(m_bstrEndpointPath,pSink); rmSink.release();
if ( FAILED(hRes) && !(hRes == WBEM_E_NOT_FOUND || hRes == WBEM_E_CALL_CANCELLED)) { // We only go here if the repository is really griping.
return WBEM_E_FAILED; }
// Check for cancellation.
if (m_bCancel) return WBEM_E_CALL_CANCELLED;
hRes = WBEM_S_NO_ERROR;
// Now get all the dynamic ones.
// =============================
for (int i = 0; i < m_aMaster.Size(); i++) { IWbemClassObject *pClass = (IWbemClassObject *) m_aMaster[i];
IWbemQualifierSet *pQSet = 0; hRes = pClass->GetQualifierSet(&pQSet); if (FAILED(hRes)) return hRes; CReleaseMe _1(pQSet);
CVARIANT v1, v2; HRESULT hResRuleBased = pQSet->Get(L"RULEBASED", 0, &v1, 0); HRESULT hResDynamic = pQSet->Get(L"DYNAMIC", 0, &v2, 0);
if (SUCCEEDED(hResDynamic)) { // If here, a normal association class.
//
// Build the query relative to this class that would select instances
// which can point to the endpoint.
// ==================================================================
// We may be able to infer that keys_only behavior is possible.
// ============================================================
IWbemContext *pCopy = 0; if (m_pContext) { if (FAILED(hRes = m_pContext->Clone(&pCopy))) return hRes; } CReleaseMe rmCtx(pCopy);
BSTR strQuery = 0; if (FAILED(hRes = NormalQ_ConstructRefsQuery(pClass, pCopy, &strQuery))) return hRes; CSysFreeMe fm(strQuery);
// The query may have been optimized out of existence.
if (hRes == WBEM_S_QUERY_OPTIMIZED_OUT) { hRes = 0; continue; }
// Now submit the query to the sink.
pSink = CreateSink(FilterForwarder_NormalRefs, strQuery); if (NULL == pSink) WBEM_E_OUT_OF_MEMORY; CReleaseMe rmSink2(pSink);
if (FAILED(hRes = CoImpersonateClient())) return hRes; OnDelete0<HRESULT(*)(void),CoRevertToSelf> RevertMe; BSTR bStrWQL = SysAllocString(L"WQL"); if (NULL == bStrWQL) return WBEM_E_OUT_OF_MEMORY;; CSysFreeMe fm_(bStrWQL); if (FAILED(hRes = m_pNs->ExecQueryAsync(bStrWQL, strQuery, 0, pCopy, pSink))) return hRes;
if (FAILED(hRes = NormalQ_LoadCheck())) return hRes; if (m_bCancel) return WBEM_E_CALL_CANCELLED; } else if (SUCCEEDED(hResRuleBased)) // Rule based
{ CFlexArray aTriads; OnDelete<CFlexArray &,void(*)(CFlexArray &),SAssocTriad::ArrayCleanup> CleanMe(aTriads);
if (FAILED(hRes = CoImpersonateClient())) return hRes;
{ OnDelete0<HRESULT(*)(void),CoRevertToSelf> RevertMe; if (FAILED(hRes = m_pNs->ManufactureAssocs(pClass, m_pEndpoint, m_pContext, v1.GetStr(), aTriads))) return hRes; }
// Now deliver stuff to sink
pSink = CreateSink(FilterForwarder_NormalRefs, L"<rulebased>"); if (NULL == pSink) return WBEM_E_OUT_OF_MEMORY; CReleaseMe rmSink2(pSink);
for (int ii = 0; ii < aTriads.Size(); ii++) { SAssocTriad *pTriad = (SAssocTriad *) aTriads[ii]; pSink->Indicate(1, &pTriad->m_pAssoc); } pSink->SetStatus(0, 0, 0, 0);
} }
return hRes; }
//****************************************************************************
//
// CAssocQuery::NormalQ_AssociatorsOf
//
// Entry point for all normal ASSOCIATORS OF queries.
//
//****************************************************************************
//
//
HRESULT CAssocQuery::NormalQ_AssociatorsOf() { HRESULT hRes; CObjectSink *pSink;
// Issue one-time call into repository for static instances.
// =========================================================
pSink = CreateSink(FilterForwarder_NormalAssocs, L"<objdb assocs request>"); if (NULL == pSink) return WBEM_E_OUT_OF_MEMORY; CReleaseMe rmSink(pSink); hRes = Db_GetInstRefs(m_bstrEndpointPath,pSink); // thows
rmSink.release();
if (FAILED(hRes) && !(hRes == WBEM_E_NOT_FOUND || hRes == WBEM_E_CALL_CANCELLED)) { // We only go here if the repository is really griping.
return WBEM_E_FAILED; }
// Check for cancellation.
if (m_bCancel) return WBEM_E_CALL_CANCELLED;
hRes = WBEM_S_NO_ERROR;
// Now get dynamic associations.
for (int i = 0; i < m_aMaster.Size(); i++) { IWbemClassObject *pClass = (IWbemClassObject *) m_aMaster[i];
IWbemQualifierSet *pQSet = 0; hRes = pClass->GetQualifierSet(&pQSet); if (FAILED(hRes)) return hRes; CReleaseMe _1(pQSet);
CVARIANT v1, v2; HRESULT hResRuleBased = pQSet->Get(L"RULEBASED", 0, &v1, 0); HRESULT hResDynamic = pQSet->Get(L"DYNAMIC", 0, &v2, 0);
if (SUCCEEDED(hResDynamic)) { // Build the query relative to this class that would select instances
// which can point to the endpoint.
BSTR strQuery = 0; hRes = NormalQ_ConstructRefsQuery(pClass, 0, &strQuery); // thows
CSysFreeMe fm(strQuery); if (FAILED(hRes)) return WBEM_E_FAILED;
if (hRes == WBEM_S_QUERY_OPTIMIZED_OUT) { hRes = 0; continue; }
CObjectSink *pInSink = CreateSink(FilterForwarder_NormalAssocs, strQuery); if (NULL == pInSink) return WBEM_E_OUT_OF_MEMORY; CReleaseMe rmInSink(pInSink);
IWbemContext *pCopy = 0; if (m_pContext) { if (FAILED(hRes = m_pContext->Clone(&pCopy))) return hRes; } CReleaseMe rmCtx(pCopy);
if (FAILED(hRes =m_pNs->MergeGetKeysCtx(pCopy))) return hRes;
if (FAILED(hRes = CoImpersonateClient())) return hRes; OnDelete0<HRESULT(*)(void),CoRevertToSelf> RevertMe; BSTR bStrWQL = SysAllocString(L"WQL"); if (NULL == bStrWQL) return WBEM_E_OUT_OF_MEMORY; CSysFreeMe fm_(bStrWQL); if (FAILED(hRes = m_pNs->ExecQueryAsync(bStrWQL, strQuery, 0, pCopy, pInSink))) return hRes;
if (FAILED(hRes = NormalQ_LoadCheck())) return hRes; if (m_bCancel) return WBEM_E_CALL_CANCELLED; } else if (SUCCEEDED(hResRuleBased)) // Rule based
{ CFlexArray aTriads; OnDelete<CFlexArray &,void(*)(CFlexArray &),SAssocTriad::ArrayCleanup> CleanMe(aTriads); if (FAILED(hRes = CoImpersonateClient())) return hRes; { OnDelete0<HRESULT(*)(void),CoRevertToSelf> RevertMe; if (FAILED(hRes = m_pNs->ManufactureAssocs(pClass, m_pEndpoint, m_pContext, v1.GetStr(), aTriads))) return hRes; }
// Now deliver stuff to sink
pSink = CreateSink(FilterForwarder_NormalRefs, L"<rulebased>"); if (NULL == pSink) return WBEM_E_OUT_OF_MEMORY; CReleaseMe rmSink2(pSink);
for (int ii = 0; ii < aTriads.Size(); ii++) { SAssocTriad *pTriad = (SAssocTriad *) aTriads[ii]; pSink->Indicate(1, &pTriad->m_pEp2); } pSink->SetStatus(0, 0, 0, 0);
} }
return hRes; }
//***************************************************************************
//
// CAssocQuery::NormalQ_GetRefsOfEndpoint
//
// Builds a query text to select association instances which can reference
// the endpoint instance.
//
// Returns:
// WBEM_S_NO_ERROR
// A WBEM_E_ code
//
//***************************************************************************
// ok
HRESULT CAssocQuery::NormalQ_ConstructRefsQuery( IN IWbemClassObject *pClass, IN OUT IWbemContext *pContextCopy, OUT BSTR *strQuery ) { if (strQuery == 0) return WBEM_E_INVALID_PARAMETER;
*strQuery = 0;
HRESULT hRes; CVARIANT v;
// Get the class name of the association we are
// trying to get instances for.
// ============================================
hRes = pClass->Get(L"__CLASS", 0, &v, 0, 0); if (FAILED(hRes)) return WBEM_E_FAILED;
// Build up the query we want.
// ===========================
WString wsQuery = "select * from "; wsQuery += V_BSTR(&v); // Add in assoc class
wsQuery += " where ";
// Next determine which role to use to reach the query endpoint.
// =============================================================
CWStringArray aNames; hRes = CanClassRefQueryEp(FALSE, pClass, &aNames); if (FAILED(hRes)) return WBEM_E_FAILED;
// If RESULTROLE is specified in the query, then eliminate
// it from aNames, since aNames is reserved for roles
// pointing to the query endpoint.
// =======================================================
LPCWSTR pszResultRole = m_Parser.GetResultRole(); if (pszResultRole) { for (int i = 0; i < aNames.Size(); i++) { if (wbem_wcsicmp(aNames[i], pszResultRole) == 0) { aNames.RemoveAt(i); i--; } } }
// Ensure something is going to point to our endpoint.
// ===================================================
if (aNames.Size() == 0) return WBEM_S_QUERY_OPTIMIZED_OUT;
// Now build up the query which refers to the endpoint explicitly.
// If more than one role works, build up an OR clause.
// ===============================================================
while (aNames.Size()) { wsQuery += aNames[0]; wsQuery += "=\"";
WString Path(m_bstrEndpointRelPath); wsQuery += Path.EscapeQuotes(); wsQuery += "\"";
aNames.RemoveAt(0); if (aNames.Size()) wsQuery += " OR "; }
// If here, we have the role to use.
// =================================
*strQuery = SysAllocString(wsQuery);
DEBUGTRACE((LOG_WBEMCORE, "Association Engine: submitting query <%S> to core\n", LPWSTR(wsQuery) ));
// Determine if association class only has keys anyway, in which
// case we can merge in the keys_only behavior. In cases
// where the provider can only enumerate instead of interpret
// the query, this might help.
// =============================================================
if (pContextCopy) { hRes = AssocClassHasOnlyKeys(pClass); if (hRes == WBEM_S_NO_ERROR) { hRes = m_pNs->MergeGetKeysCtx(pContextCopy); } }
return WBEM_S_NO_ERROR; }
//****************************************************************************
//
// CAssocQuery::AssocClassHasOnlyKeys
//
// Returns WBEM_S_NO_ERROR if the assoc class only has keys.
//
//****************************************************************************
//
HRESULT CAssocQuery::AssocClassHasOnlyKeys( IN IWbemClassObject *pObj ) { int nKeyCount = 0; HRESULT hRes;
pObj->BeginEnumeration(WBEM_FLAG_KEYS_ONLY); while (1) { hRes = pObj->Next(0, 0, 0, 0, 0); if (hRes == WBEM_S_NO_MORE_DATA) break; nKeyCount++; } pObj->EndEnumeration();
CVARIANT v; hRes = pObj->Get(L"__PROPERTY_COUNT", 0, &v, 0, 0); if (FAILED(hRes) || v.GetType() != VT_I4) return WBEM_E_FAILED;
if (V_I4(&v) == nKeyCount) return WBEM_S_NO_ERROR;
return WBEM_E_FAILED; }
//****************************************************************************
//
// CAssocQuery::FilterFowarder_NormalRefs
//
// Filtering and forwarding for REFERENCES OF queries.
// Handles normal queries and CLASSDEFSONLY queries; not used for
// SCHEMAONLY queries.
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::FilterForwarder_NormalRefs( IN IWbemClassObject *pCandidate ) { BOOL bKeep = TRUE; HRESULT hRes = 0;
if (pCandidate == 0) return WBEM_E_INVALID_PARAMETER;
// All objects must be instances. We filter out any
// class definitions.
// ==================================================
CVARIANT vGenus; pCandidate->Get(L"__GENUS", 0, &vGenus, 0, 0); if (vGenus.GetType() == VT_I4 && LONG(vGenus) == 1) return WBEM_S_NO_ERROR;
// The object must pass a security check.
// ======================================
hRes = AccessCheck((CWbemObject *) pCandidate); if (FAILED(hRes)) return WBEM_S_NO_ERROR;
// RESULTCLASS test
// ================
LPCWSTR pszResultClass = m_Parser.GetResultClass(); if (pszResultClass) { hRes = St_ObjIsOfClass(pszResultClass, pCandidate); if (FAILED(hRes)) bKeep = FALSE; }
// Verify the association points to the endpoint and
// if so, get the role via which it does so.
// ==================================================
BSTR strRole = 0; hRes = DoesAssocInstRefQueryEp(pCandidate,&strRole); CSysFreeMe _1(strRole);
if (FAILED(hRes)) bKeep = FALSE;
// ROLE
LPCWSTR pszRole = m_Parser.GetRole(); if (pszRole && strRole) { if (wbem_wcsicmp(pszRole, strRole) != 0) bKeep = FALSE; }
// REQUIREDQUALIFIER test
LPCWSTR pszRequiredQual = m_Parser.GetRequiredQual(); if (pszRequiredQual) { hRes = St_ObjHasQualifier(pszRequiredQual, pCandidate); if (FAILED(hRes)) bKeep = FALSE; }
if (!bKeep) return WBEM_S_NO_ERROR;
// If here, the object is a candidate. If the query type
// is not CLASSDEFSONLY, then we directly send it back.
// ======================================================
if ((m_Parser.GetQueryType() & QUERY_TYPE_CLASSDEFS_ONLY) == 0) { hRes = m_pDestSink->Indicate(1, &pCandidate); return hRes; }
IWbemClassObject *pRetCls = NULL; { CInCritSec ics(&m_csDeliveredAccess); hRes = GetClassDefsOnlyClass(pCandidate, &pRetCls); } CReleaseMe rmRetClass(pRetCls);
// We may already have delivered the class in question,
// so we don't just assume there is a pointer here.
// ====================================================
if (SUCCEEDED(hRes) && pRetCls) { hRes = m_pDestSink->Indicate(1, &pRetCls); }
if (FAILED(hRes)) return hRes;
return WBEM_S_OPERATION_CANCELLED; }
//****************************************************************************
//
// CAssocQuery::FilterForwarder_NormalAssocs
//
// First level association instance filtering for ASSOCIATORS OF queries.
// Handles normal queries and CLASSDEFSONLY queries; not used for
// SCHEMAONLY queries.
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::FilterForwarder_NormalAssocs( IN IWbemClassObject *pAssocInst ) { HRESULT hRes = 0; BOOL bKeep = TRUE;
if (pAssocInst == 0) return WBEM_E_INVALID_PARAMETER;
// All objects must be instances. We filter out any
// class definitions.
// ==================================================
CVARIANT vGenus; pAssocInst->Get(L"__GENUS", 0, &vGenus, 0, 0); if (vGenus.GetType() == VT_I4 && LONG(vGenus) == 1) return WBEM_S_NO_ERROR;
// The object must pass a security check.
// ======================================
hRes = AccessCheck((CWbemObject *) pAssocInst); if (FAILED(hRes)) return WBEM_S_NO_ERROR;
// ASSOCCLASS
// ==========
LPCWSTR pszAssocClass = m_Parser.GetAssocClass(); if (pszAssocClass) { hRes = St_ObjIsOfClass(pszAssocClass, pAssocInst); if (FAILED(hRes)) bKeep = FALSE; }
// REQUIREDASSOCQUALIFIER
// ======================
LPCWSTR pszRequiredAssocQual = m_Parser.GetRequiredAssocQual(); if (pszRequiredAssocQual) { hRes = St_ObjHasQualifier(pszRequiredAssocQual, pAssocInst); if (FAILED(hRes)) bKeep = FALSE; }
// ROLE
// ====
LPCWSTR pszRole = m_Parser.GetRole(); if (pszRole) { hRes = RoleTest(m_pEndpoint, pAssocInst, m_pNs, pszRole, ROLETEST_MODE_PATH_VALUE); if (FAILED(hRes)) bKeep = FALSE; }
// If we have already rejected the instance, just give up without going any further.
// =================================================================================
if (bKeep == FALSE) return WBEM_S_NO_ERROR;
// If here, looks like we'll be in the business of actually getting
// the other endpoint. Other rejections are still possible based
// on RESULTROLE, however.
// ================================================================
// Get the Unknown Ep role.
// ========================
hRes = WBEM_S_NO_ERROR;
// By keeping track of the last property we enumed, we will be able to handle
// associations with multiple endpoints. (sanjes)
// ==========================================================================
BOOL bQueryEndpointFound = FALSE;
pAssocInst->BeginEnumeration(WBEM_FLAG_REFS_ONLY); OnDeleteObj0<IWbemClassObject, HRESULT(__stdcall IWbemClassObject:: *)(void),IWbemClassObject::EndEnumeration> EndMe(pAssocInst);
while (hRes == WBEM_S_NO_ERROR) { // Make sure these are reinitialized on each loop.
BSTR strUnkEpPath = 0, strUnkEpRole = 0; bKeep = TRUE;
// Just keep passing in the last property we got
// ==============================================
hRes = GetUnknownEpRoleAndPath(pAssocInst, &bQueryEndpointFound, &strUnkEpRole, &strUnkEpPath ); auto_bstr rmUnkEpRole(strUnkEpRole); auto_bstr rmUnkEpPath(strUnkEpPath); if (FAILED(hRes)) return hRes;; if (hRes == WBEM_S_NO_MORE_DATA) break; // If we ran out of properties we should quit.
// ===========================================
if (SUCCEEDED(hRes)) { // Verify the RESULTROLE.
// ======================
LPCWSTR pszResultRole = m_Parser.GetResultRole(); if (pszResultRole) { if (wbem_wcsicmp(pszResultRole, rmUnkEpRole.get()) != 0) bKeep = FALSE; }
// If here, we have the path of the unknown endpoint.
// We save it away in a protected array.
// ==================================================
if (bKeep) hRes = AddEpCandidatePath(rmUnkEpPath.release()); // Acquires pointer
} }
return WBEM_S_NO_ERROR; }
//****************************************************************************
//
// CAssocQuery::AddEpCandidatePath
//
// Adds the path to a candidate endpoint. This is an intermediate
// step in an ASSOCIATORS OF query.
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::AddEpCandidatePath( IN BSTR strOtherEp ) { CInCritSec ics(&m_csCandidateEpAccess);
if (CFlexArray::no_error != m_aEpCandidates.Add(strOtherEp)) { SysFreeString(strOtherEp); return WBEM_E_OUT_OF_MEMORY; }
return WBEM_S_NO_ERROR; }
//****************************************************************************
//
// CAssocQuery::EmptyCandidateEpArray
//
// Empties the Endpoint candidate array.
//
//****************************************************************************
// visual ok
void CAssocQuery::EmptyCandidateEpArray() { CInCritSec ics(&m_csCandidateEpAccess);
for (int i = 0; i < m_aEpCandidates.Size(); i++) SysFreeString((BSTR) m_aEpCandidates[i]); m_aEpCandidates.Empty(); }
//****************************************************************************
//
// CAssocQuery::PerformFinalEpTests
//
// Performs all final filter tests on the query endpoint.
//
// Returns
// WBEM_S_NO_ERROR if the object should be retained.
// WBEM_E_INVALID_OBJECT if the object should not be retained.
//
//****************************************************************************
//
HRESULT CAssocQuery::PerformFinalEpTests( IWbemClassObject *pEp ) { BOOL bKeep = TRUE; HRESULT hRes;
// Perform final tests. RESULTROLE
// was verified in the intermediate stage.
// =======================================
LPCWSTR pszResultClass = m_Parser.GetResultClass(); if (pszResultClass) { hRes = St_ObjIsOfClass(pszResultClass, pEp); if (FAILED(hRes)) bKeep = FALSE; }
// REQUIREDQUALIFIER test
// =======================
LPCWSTR pszRequiredQual = m_Parser.GetRequiredQual(); if (pszRequiredQual) { hRes = St_ObjHasQualifier(pszRequiredQual, pEp); if (FAILED(hRes)) bKeep = FALSE; }
if (bKeep) return WBEM_S_NO_ERROR;
return WBEM_E_INVALID_OBJECT; }
//****************************************************************************
//
// CAssocQuery::ResolvePathsToObjects
//
//****************************************************************************
//
HRESULT CAssocQuery::EpClassTest( LPCWSTR pszResultClass, LPCWSTR strClassName, IWbemClassObject *pTestClass ) { HRESULT hRes;
if (pszResultClass == 0 || strClassName == 0 || pTestClass == 0) return WBEM_E_INVALID_PARAMETER;
if (wbem_wcsicmp(pszResultClass, strClassName) == 0) return WBEM_S_NO_ERROR;
// Check the derivation of the class and see if the result class is mentioned.
// ===========================================================================
CVARIANT v; hRes = pTestClass->Get(L"__DERIVATION", 0,&v, 0, 0); if (FAILED(hRes)) return hRes;
CSAFEARRAY sa((SAFEARRAY *) v); v.Unbind();
int nNum = sa.GetNumElements();
for (int j = 0; j < nNum; j++) { BSTR bstrCls = 0; if (FAILED(sa.Get(j, &bstrCls))) return WBEM_E_OUT_OF_MEMORY; CSysFreeMe _(bstrCls); if (wbem_wcsicmp(bstrCls, pszResultClass) == 0) return WBEM_S_NO_ERROR; }
return WBEM_E_NOT_FOUND; }
//****************************************************************************
//
// CAssocQuery::ResolvePathsToObjects
//
// Runs through the existing endpoints and gets the objects, passes them
// through the final tests sends them to to the caller.
//
// Autoassoc support can directly populate the m_aEpCandidates array.
//
// <nMaxToProcess> If -1, process all. Otherwise, only process
// as many as are requested.
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::ResolveEpPathsToObjects( IN int nMaxToProcess ) { if (ConfigMgr::ShutdownInProgress()) return WBEM_E_SHUTTING_DOWN;
HRESULT hRes = WBEM_S_NO_ERROR; IWbemClassObject *pEp = NULL;
// If the query type is CLASSDEFS only, reduce the Ep list
// to a list of class definitions.
// =======================================================
if (m_Parser.GetQueryType() & QUERY_TYPE_CLASSDEFS_ONLY) ConvertEpListToClassDefsOnly();
// Determine how much of the ep list to process.
// =============================================
int nArraySize; { CInCritSec ics(&m_csCandidateEpAccess); nArraySize = m_aEpCandidates.Size();
if (nMaxToProcess == -1 || nMaxToProcess > nArraySize) nMaxToProcess = nArraySize; }
// RESULTCLASS test
// ================
LPCWSTR pszResultClass = m_Parser.GetResultClass();
// Process each element in EP list.
// ================================
for (int i = 0; i < nMaxToProcess; i++) { pEp = 0;
// Extract one endpoint.
// =====================
BSTR strEpPath = NULL; { CInCritSec ics(&m_csCandidateEpAccess); strEpPath = (BSTR) m_aEpCandidates[0]; m_aEpCandidates.RemoveAt(0); } CSysFreeMe _2(strEpPath);
// Do some analysis on the path.
// =============================
BSTR strClassName = 0; BOOL bIsClass; hRes = St_ObjPathInfo(strEpPath, &strClassName, &bIsClass); if (FAILED(hRes)) { hRes = 0; continue; }
BOOL bKeep = TRUE;
CSysFreeMe _1(strClassName);
// Important optimization: If RESULTCLASS is specified, look
// up the class definition before trying to get the endpoint
// just in case it can't pass the test.
// ==========================================================
if (pszResultClass) { // search for RESULTCLASS in derivation list of class from path
// if not search in derived classes from it
// Get the class and do a RESULTCLASS test to avoid
// getting the object.
// =================================================
IWbemClassObject *pTestClass; hRes = GetClassFromAnywhere(strClassName, 0, &pTestClass);
if (FAILED(hRes)) { DEBUGTRACE((LOG_WBEMCORE, "Association cannot find class <%S> hr = %08x\n",strClassName,hRes)); hRes = 0; continue; } CReleaseMe _11(pTestClass);
// Make sure the endpoint class passes query tests.
// =================================================
hRes = EpClassTest(pszResultClass, strClassName, pTestClass); if (FAILED(hRes)) { IWbemClassObject *pTestResultClass; hRes = GetClassFromAnywhere(pszResultClass, 0, &pTestResultClass); if (FAILED(hRes)) { DEBUGTRACE((LOG_WBEMCORE, "Association cannot find class <%S> hr %08x\n", pszResultClass, hRes)); hRes = 0; continue; } CReleaseMe _111(pTestResultClass); hRes = EpClassTest(strClassName, pszResultClass, pTestResultClass); if (FAILED(hRes)) { hRes = WBEM_S_NO_ERROR; continue;
}
} }
// If a class, use our high-speed class getter.
// ============================================
if (bIsClass) { // GetClassFromAnyWhere
hRes = GetClassFromAnywhere(strClassName, strEpPath, &pEp); if (FAILED(hRes)) { DEBUGTRACE((LOG_WBEMCORE, "Association cannot resolve dangling reference <%S> hr = %08x\n",strEpPath,hRes)); hRes = 0; continue; } }
// Otherwise, an instance and we go the slow route.
// ================================================
else // An instance
{
IWbemClassObject* pErrorObj = NULL; hRes = m_pNs->Exec_GetObjectByPath( strEpPath, 0, // Flags
m_pContext, // Context
&pEp, // Result obj
&pErrorObj // Error obj, if any
);
CReleaseMe _11(pErrorObj);
if (FAILED(hRes)) { DEBUGTRACE((LOG_WBEMCORE, "Association cannot resolve reference <%S> hr = %08x\n",strEpPath,hRes)); hRes = 0; continue; } }
// So, do we actually have an object, or are we fooling
// ourselves?
// =====================================================
if (!pEp) { hRes = 0; continue; }
// The object must pass a security check.
// ======================================
hRes = AccessCheck((CWbemObject *) pEp); if (FAILED(hRes)) { pEp->Release(); hRes = 0; continue; }
// If we are going to keep this, send it to the caller.
// ====================================================
hRes = PerformFinalEpTests(pEp);
if (SUCCEEDED(hRes)) hRes = m_pDestSink->Indicate(1, &pEp);
pEp->Release(); hRes = WBEM_S_NO_ERROR; }
return hRes; }
//****************************************************************************
//
// CAssocQuery::St_ObjPathPointsToClass
//
// Returns WBEM_S_NO_ERROR if the object path points to a class,
// or WBEM_E_FAILED if not. Can also return codes for invalid paths,
// out of memory, etc.
//
//****************************************************************************
//
HRESULT CAssocQuery::St_ObjPathInfo( IN LPCWSTR pszPath, OUT BSTR *pszClass, OUT BOOL *pbIsClass ) { CObjectPathParser Parser; ParsedObjectPath* pParsedPath = NULL;
if (pszPath == 0) return WBEM_E_INVALID_PARAMETER;
int nRes = Parser.Parse(pszPath, &pParsedPath);
if (nRes != CObjectPathParser::NoError || pParsedPath->m_pClass == NULL) { // Fatal. Bad path in association.
return WBEM_E_INVALID_OBJECT_PATH; }
if (pbIsClass) { if (pParsedPath->m_dwNumKeys == 0) *pbIsClass = TRUE; else *pbIsClass = FALSE; }
if (pszClass && pParsedPath->m_pClass) *pszClass = SysAllocString(pParsedPath->m_pClass);
Parser.Free(pParsedPath);
return WBEM_S_NO_ERROR; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// END NORMAL QUERY SUPPORT
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//***************************************************************************
//
//***************************************************************************
//
//***************************************************************************
//
// CAssocQuery::CanClassRefReachQueryEp
//
// Determines whether the property to which pQSet is bound can reach
// the query endpoint via a CLASSREF qualifier.
//
// <pQSet> bound to the property which supposedly references the query
// endpoint.
// <bStrict> If true, the reference must directly reference the query
// endpoint class, if FALSE it may reference any of the superclasses.
//
// Returns:
// WBEM_S_NO_ERROR if the reference occurs.
// WBEM_E_NOT_FOUND if the references does not occur.
//
//***************************************************************************
// visual ok
HRESULT CAssocQuery::CanClassRefReachQueryEp( IWbemQualifierSet *pQSet, BOOL bStrict ) { HRESULT hRes; CVARIANT v; hRes = pQSet->Get(L"CLASSREF", 0, &v, 0); if (FAILED(hRes)) return WBEM_E_NOT_FOUND;
if (V_VT(&v) != (VT_BSTR | VT_ARRAY)) return WBEM_E_INVALID_OBJECT;
CSAFEARRAY sa((SAFEARRAY *) v); v.Unbind();
int nNum = sa.GetNumElements();
// Iterate through the safearray.
// ==============================
for (int i = 0; i < nNum; i++) { BSTR bstrClass = 0; if (FAILED(sa.Get(i, &bstrClass))) return WBEM_E_OUT_OF_MEMORY; if (bstrClass == 0) continue; CSysFreeMe _(bstrClass); if (bStrict) { if (wbem_wcsicmp(bstrClass, m_bstrEndpointClass) == 0) return WBEM_S_NO_ERROR; } else for (int i2 = 0; i2 < m_aEndpointHierarchy.Size(); i2++) { if (wbem_wcsicmp(bstrClass, m_aEndpointHierarchy[i2]) == 0) return WBEM_S_NO_ERROR; } }
return WBEM_E_NOT_FOUND; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// BEGIN HELPER FUNCTIONS
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//****************************************************************************
//
// CAssocQuery::St_GetObjectInfo
//
// Returns info about the object, such as its path, class, and
// class hierarchy.
//
//****************************************************************************
// ok
HRESULT CAssocQuery::St_GetObjectInfo( IN IWbemClassObject *pObj, OUT BSTR *pClass, OUT BSTR *pRelpath, OUT BSTR *pPath, OUT CWStringArray &aHierarchy ) { HRESULT hRes; int nRes; CVARIANT v;
if (!pObj) return WBEM_E_INVALID_PARAMETER;
// Get the owning class.
// =====================
hRes = pObj->Get(L"__CLASS", 0, &v, 0, 0); if (FAILED(hRes)) return hRes;
if (V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_OBJECT;
nRes = aHierarchy.Add(LPWSTR(v)); if (nRes) return WBEM_E_OUT_OF_MEMORY;
if (pClass) { *pClass = V_BSTR(&v); v.Unbind(); } v.Clear();
// Get the rel path.
// =================
if (pRelpath) { hRes = pObj->Get(L"__RELPATH", 0, &v, 0, 0); if (FAILED(hRes)) return hRes; if ( VT_BSTR != V_VT(&v)) return WBEM_E_INVALID_OBJECT; *pRelpath = V_BSTR(&v); v.Unbind(); } v.Clear();
if (pPath) { hRes = pObj->Get(L"__PATH", 0, &v, 0, 0); if (FAILED(hRes)) return hRes; *pPath = V_BSTR(&v); v.Unbind(); } v.Clear();
// Get the superclasses.
// =====================
hRes = pObj->Get(L"__DERIVATION", 0,&v, 0, 0); if (FAILED(hRes)) return hRes;
CSAFEARRAY sa((SAFEARRAY *) v); v.Unbind();
int nNum = sa.GetNumElements();
for (int j = 0; j < nNum; j++) { BSTR bstrClass = 0; nRes = sa.Get(j, &bstrClass); if (FAILED(nRes)) return WBEM_E_OUT_OF_MEMORY;
CSysFreeMe _(bstrClass); nRes = aHierarchy.Add(bstrClass); if (nRes) return WBEM_E_OUT_OF_MEMORY; }
return WBEM_S_NO_ERROR; }
//****************************************************************************
//
// CAssocQuery::GetUnknownEpRoleAndPath
//
// Given an association object (class or inst), returns the role and
// path which references the unknown endpoint.
//
// Calling code is responsible for calling BeginEnum/EndEnum
//
// All the OUT parameters are optional.
//
//****************************************************************************
//
HRESULT CAssocQuery::GetUnknownEpRoleAndPath( IN IWbemClassObject *pAssoc, IN BOOL *pFoundQueryEp, OUT BSTR *pszRole, OUT BSTR *pszUnkEpPath ) { HRESULT hRes = WBEM_E_FAILED;
if (pAssoc == 0) return WBEM_E_INVALID_PARAMETER;
// Loop through the properties trying to find a legitimate
// reference to our endpoint class.
// =======================================================
// sanjes
// pAssoc->BeginEnumeration(WBEM_FLAG_REFS_ONLY);
while (1) { BSTR strPropName = 0; hRes = pAssoc->Next(0, &strPropName, 0, 0, 0); CSysFreeMe _1(strPropName);
if (hRes == WBEM_S_NO_MORE_DATA) break;
hRes = RoleTest(m_pEndpoint, pAssoc, m_pNs, strPropName, ROLETEST_MODE_PATH_VALUE); if (SUCCEEDED(hRes) && *pFoundQueryEp == FALSE) // The query ep
{ *pFoundQueryEp = TRUE; continue; }
// If here, we found the prop name which apparently references the
// other endpoint.
// ===============================================================
if (pszRole) { *pszRole = SysAllocString(strPropName); if (NULL == *pszRole) return WBEM_E_OUT_OF_MEMORY; }
CVARIANT vPath; hRes = pAssoc->Get(strPropName, 0, &vPath, 0, 0); if (FAILED(hRes) || vPath.GetType() != VT_BSTR) break;
if (pszUnkEpPath) { *pszUnkEpPath = SysAllocString(vPath.GetStr()); if (NULL == *pszUnkEpPath) return WBEM_E_OUT_OF_MEMORY; } hRes = WBEM_S_NO_ERROR; break; }
// sanjes
// pAssoc->EndEnumeration();
return hRes; // Unexpected in real life
}
//****************************************************************************
//
// CAssocQuery::RoleTest
//
// Determines if the <pCandidate> object can point to the <pEndpoint> object
// via the specified <pszRole> property.
//
// Parameters:
// <pEndpoint> The test endpoint object
// <pCandidate> The association object which may point to the endpoint.
// <pszRole> The role to use for the test.
// <dwMode> One of the ROLETEST_MODE_ constants.
//
// Precisely,
//
// (1) ROLETEST_MODE_PATH_VALUE
// The candidate must reference the endpoint exactly via the specified
// role property, which must contain the path of the endpoint.
// Requirement: Both <pEndpoint> <pCandidate> can be anything.
//
// (2) ROLETEST_MODE_CIMREF_TYPE
// The role path is NULL and the CIM reference type is used to determine
// if the endpoint can be referenced. In this case, the CIM reference
// type must exactly reference the endpoint class.
// Requirement: Both <pEndpoint> and <pCandidate> are classes.
//
// Returns:
// WBEM_S_NO_ERROR
// WBEM_E_NOT_FOUND If the role cannot reference the endpoint.
// WBEM_E_INVALID_PARAMETER ...in most other cases.
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::RoleTest( IN IWbemClassObject *pEndpoint, IN IWbemClassObject *pCandidate, IN CWbemNamespace *pNs, IN LPCWSTR pszRole, IN DWORD dwMode ) { HRESULT hRes; CVARIANT v; BOOL bEndpointIsClass, bCandidateIsClass;
if (!pszRole || !pEndpoint || !pCandidate) return WBEM_E_INVALID_PARAMETER;
// Get the genus values of the endpoint & candidate.
// =================================================
pEndpoint->Get(L"__GENUS", 0, &v, 0, 0); if (v.GetLONG() == 1) bEndpointIsClass = TRUE; else bEndpointIsClass = FALSE; v.Clear();
pCandidate->Get(L"__GENUS", 0, &v, 0, 0); if (v.GetLONG() == 1) bCandidateIsClass = TRUE; else bCandidateIsClass = FALSE; v.Clear();
// Get the qualifier set for the specified <role> property.
// ========================================================
IWbemQualifierSet *pQSet = 0; hRes = pCandidate->GetPropertyQualifierSet(pszRole, &pQSet); if (FAILED(hRes)) return WBEM_E_NOT_FOUND; CReleaseMe _1(pQSet);
// Now, get the type of the role.
// ==============================
CVARIANT vCimType; hRes = pQSet->Get(L"CIMTYPE", 0, &vCimType, 0); if (FAILED(hRes) || V_VT(&vCimType) != VT_BSTR) return WBEM_E_FAILED;
// Get the class name from it.
// ===========================
wchar_t ClassName[MAX_CLASS_NAME]; *ClassName = 0; BSTR strRefClass = V_BSTR(&vCimType); if (strRefClass) { if (wcslen_max(strRefClass,MAX_CLASS_NAME) > MAX_CLASS_NAME) return WBEM_E_FAILED; parse_REF(strRefClass,MAX_CLASS_NAME,ClassName); } // Once here, 'object ref' types will simply
// have a zero-length <ClassName> string.
// Determine which of the four cases we are executing.
// ===================================================
if (dwMode == ROLETEST_MODE_CIMREF_TYPE) { if (bCandidateIsClass == FALSE && bEndpointIsClass == FALSE) return WBEM_E_INVALID_PARAMETER;
if (*ClassName == 0) return WBEM_E_NOT_FOUND;
// See if the class name and the class of the object
// are the same.
// ==================================================
CVARIANT vCls; HRESULT hResInner = pEndpoint->Get(L"__CLASS", 0, &vCls, 0, 0); if (FAILED(hResInner)) return hResInner;
if (wbem_wcsicmp(ClassName, vCls.GetStr()) == 0) return WBEM_S_NO_ERROR;
// Find out if the CIM type string points to the object.
// =====================================================
hRes = PathPointsToObj(ClassName, pEndpoint, pNs); }
// The endpoint must be directly and exactly referenced
// by the role property's *value*.
// ====================================================
else if (dwMode == ROLETEST_MODE_PATH_VALUE) { // Get the value of the role property.
// ===================================
CVARIANT vRolePath; hRes = pCandidate->Get(pszRole, 0, &vRolePath, 0, 0); if (FAILED(hRes)) return WBEM_E_FAILED;
if (vRolePath.GetType() == VT_NULL) return WBEM_E_NOT_FOUND;
hRes = PathPointsToObj(vRolePath.GetStr(), pEndpoint, pNs); } else return WBEM_E_INVALID_PARAMETER;
return hRes; }
//****************************************************************************
//
// CAssocQuery::St_ObjIsOfClass
//
// Determines if the specified object is of or derives from the specified
// class.
//
// Returns:
// WBEM_E_INVALID_CLASS if there is no match.
// WBEM_S_NO_ERROR if there is a match.
// WBEM_E_* on other failures
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::St_ObjIsOfClass( IN LPCWSTR pszRequiredClass, IN IWbemClassObject *pObj ) { if (pszRequiredClass == 0) return WBEM_E_INVALID_PARAMETER;
HRESULT hRes; CWStringArray aHierarchy;
hRes = St_GetObjectInfo(pObj, 0, 0, 0, aHierarchy); if (FAILED(hRes)) return hRes;
for (int i = 0; i < aHierarchy.Size(); i++) if (wbem_wcsicmp(pszRequiredClass, aHierarchy[i]) == 0) return WBEM_S_NO_ERROR;
return WBEM_E_INVALID_CLASS; }
//****************************************************************************
//
// CAssocQuery::PathPointsToObj
//
// Determines if a particular object path points to the specified object
// or not. Tries to avoid full object path parsing, if possible.
//
// Returns WBEM_S_NO_ERROR, WBEM_E_FAILED
//
//****************************************************************************
// ok
HRESULT CAssocQuery::PathPointsToObj( IN LPCWSTR pszPath, IN IWbemClassObject *pObj, IN CWbemNamespace *pNs ) { HRESULT hRes;
if (pszPath == 0 || pObj == 0) return WBEM_E_INVALID_PARAMETER;
// Test for simple equality of __RELPATH.
// ======================================
CVARIANT vRel; hRes = pObj->Get(L"__RELPATH", 0, &vRel, 0, 0); if (FAILED(hRes) || VT_BSTR != V_VT(&vRel)) return WBEM_E_FAILED;
if (wbem_wcsicmp(pszPath, V_BSTR(&vRel)) == 0) return WBEM_S_NO_ERROR;
// Test for simple equality of __PATH.
// ===================================
CVARIANT vFullPath; hRes = pObj->Get(L"__PATH", 0, &vFullPath, 0, 0); if (FAILED(hRes) || VT_BSTR != V_VT(&vRel)) return WBEM_E_FAILED;
if (wbem_wcsicmp(pszPath, V_BSTR(&vFullPath)) == 0) return WBEM_S_NO_ERROR;
// If here, we have to actually parse the object paths
// in question.
// ===================================================
LPWSTR pszNormalizedPath = CQueryEngine::NormalizePath(pszPath, pNs); LPWSTR pszNormalizedTargetPath = CQueryEngine::NormalizePath(vFullPath.GetStr(), pNs); CDeleteMe <wchar_t> _1(pszNormalizedPath); CDeleteMe <wchar_t> _2(pszNormalizedTargetPath);
if (pszNormalizedPath && pszNormalizedTargetPath) if (wbem_wcsicmp(pszNormalizedPath, pszNormalizedTargetPath) == 0) return WBEM_S_NO_ERROR;
return WBEM_E_FAILED; }
//***************************************************************************
//
// CAssocQualifierL::St_ObjHasQualifier
//
// Determines if an object has a particular qualifier. Used for
// REQUIREDQUALIFIER or REQUIREDASSOCQUALIFIER tests. The qualifier
// must be present and not be VARIANT_FALSE.
// Returns WBEM_S_NO_ERROR if the object has the qualifier.
// Returns an WBEM_E_ error code otherwise.
//
//***************************************************************************
// visual ok
HRESULT CAssocQuery::St_ObjHasQualifier( IN LPCWSTR pszQualName, IN IWbemClassObject *pObj ) { if (pszQualName == 0 || wcslen(pszQualName) == 0 || pObj == 0) return WBEM_E_INVALID_PARAMETER;
IWbemQualifierSet *pQSet = 0;
HRESULT hRes = pObj->GetQualifierSet(&pQSet); if (FAILED(hRes)) return WBEM_E_FAILED; CReleaseMe _1(pQSet);
CVARIANT v; hRes = pQSet->Get(pszQualName, 0, &v, 0);
if (SUCCEEDED(hRes)) { if (V_VT(&v) == VT_BOOL && V_BOOL(&v) == VARIANT_FALSE) return WBEM_E_FAILED; return WBEM_S_NO_ERROR; }
return WBEM_E_FAILED; }
//***************************************************************************
//
// CAssocQuery::St_ReleaseArray
//
//***************************************************************************
// visual ok
HRESULT CAssocQuery::St_ReleaseArray( IN CFlexArray &aObjects ) { // Release all the objects.
// ========================
for (int i = 0; i < aObjects.Size(); i++) { IWbemClassObject *p = (IWbemClassObject *) aObjects[i]; p->Release(); }
return WBEM_S_NO_ERROR; }
//****************************************************************************
//
// CAssocQuery::Db_GetClass
//
// DB abstraction layer. Will make it easier to plug in Quasar engine.
//
//****************************************************************************
// ok
HRESULT CAssocQuery::Db_GetClass( IN LPCWSTR pszClassName, OUT IWbemClassObject **pClass ) { HRESULT hRes = CRepository::GetObject( m_pNs->GetNsSession(), m_pNs->GetScope(), pszClassName, 0, pClass );
return hRes; }
//****************************************************************************
//
// CAssocQuery::Db_GetInstRefs
//
// DB abstraction layer. Will make it easier to plug in Quasar engine.
//
//****************************************************************************
//
HRESULT CAssocQuery::Db_GetInstRefs( IN LPCWSTR pszTargetObj, IN IWbemObjectSink *pSink ) { HRESULT hRes = CRepository::GetInstanceRefs( m_pNs->GetNsSession(), m_pNs->GetScope(), pszTargetObj, pSink );
return hRes; }
//****************************************************************************
//
// CAssocQuery::Db_GetClass
//
// DB abstraction layer. Will make it easier to plug in Quasar engine.
//
//****************************************************************************
// ok
HRESULT CAssocQuery::Db_GetRefClasses( IN LPCWSTR pszClass, OUT CWStringArray &aRefClasses ) { HRESULT hRes = CRepository::GetRefClasses( m_pNs->GetNsSession(), m_pNs->GetNsHandle(), pszClass, FALSE, aRefClasses );
return hRes; }
//***************************************************************************
//
// CAssocQuery::Db_GetClassRefClasses
//
// Gets all classes with HasClassRefs qualifiers.
//
//***************************************************************************
//
HRESULT CAssocQuery::Db_GetClassRefClasses( IN CFlexArray &aDest ) { HRESULT hRes; CSynchronousSink* pRefClassSink = CSynchronousSink::Create(); if (NULL == pRefClassSink) return WBEM_E_OUT_OF_MEMORY; pRefClassSink->AddRef(); CReleaseMe _1(pRefClassSink);
hRes = CRepository::GetClassesWithRefs(m_pNs->GetNsSession(),m_pNs->GetNsHandle(),pRefClassSink); if (FAILED(hRes)) return hRes; pRefClassSink->GetStatus(&hRes, NULL, NULL); if (FAILED(hRes)) return hRes;
aDest.Bind(pRefClassSink->GetObjects().GetArray());
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::GetClassFromAnywhere
//
// Tries to get a class definition from anywhere as fast as possible.
// We do this by the following algorithm in a hope to achieve best
// performance:
//
// (1) Search the dynamic class cache
// (2) Call the database directly for this namespace
// (3) Call Exec_GetObjectByPath (hoping that an unrelated dyn class
// provider has the class)
//
//***************************************************************************
// visual ok
HRESULT CAssocQuery::GetClassFromAnywhere( IN LPCWSTR pszEpClassName, IN LPCWSTR pszFullClassPath, OUT IWbemClassObject **pCls ) { HRESULT hRes;
// Try to find the class in the dynamic class cache.
// We will only look for classes in our own namespace, however.
// ============================================================
hRes = GetDynClass(pszEpClassName, pCls);
if (SUCCEEDED(hRes)) return hRes;
// If here, no luck in the dynamic class cache. Try
// the repository. We try to use the full path to support
// the limited cross-namespace support required by
// SNMP SMIR, etc.
// ========================================================
if (pszFullClassPath == 0) pszFullClassPath = pszEpClassName;
hRes = Db_GetClass(pszFullClassPath, pCls);
if (SUCCEEDED(hRes)) return hRes;
// If here, our hopes are nearly dashed. One last chance
// that a dyn class provider may have it if the
// class was supplied by a provider other than the
// one which supplied the association class.
// =====================================================
IWbemClassObject* pErrorObj = NULL;
hRes = m_pNs->Exec_GetObjectByPath( (LPWSTR) pszFullClassPath, // Class name
0, // Flags
m_pContext, // Context
pCls, // Result obj
&pErrorObj // Error obj, if any
);
CReleaseMe _1(pErrorObj);
// If we found it, great.
// ======================
if (SUCCEEDED(hRes)) return hRes;
return WBEM_E_NOT_FOUND; }
//***************************************************************************
//
// CAssocQuery::St_HasClassRefs
//
// Determines if a class has a <HasClassRefs> qualifier.
//
// Parameters
// <pCandidate> Points to the object to be tested (read-only).
//
// Return value:
// WBEM_S_NO_ERROR If the class has a <HasClassRefs> qualifier.
// WBEM_E_NOT_FOUND If the class doesn't have the qualifier.
// ...other codes
//
//***************************************************************************
// visual ok
HRESULT CAssocQuery::St_HasClassRefs( IN IWbemClassObject *pCandidate ) { if (pCandidate == 0) return WBEM_E_INVALID_PARAMETER;
HRESULT hRes = St_ObjHasQualifier(L"HasClassRefs", pCandidate); if (SUCCEEDED(hRes)) return WBEM_S_NO_ERROR;
return WBEM_E_NOT_FOUND; }
//***************************************************************************
//
// CAssocQuery::AccessCheck
//
// Does a security check on a static object to make sure that the user
// should see it.
//
// If the object is in the current namespace anyway, we short-circuit
// and allow it without a lot of hassle. The guy is obviously one of us
// and should be allowed to proceed unhindered. In those weird cases where
// the object was from a foreign namespace, we have to play INS and check
// on him.
//
//***************************************************************************
HRESULT CAssocQuery::AccessCheck( IWbemClassObject *pSrc ) { if (pSrc == 0) return WBEM_E_INVALID_PARAMETER;
CWbemObject *pObj = (CWbemObject *) pSrc;
// Easy case is 9x box where user is cleared for everything
// ========================================================
if((m_pNs->GetSecurityFlags() & SecFlagWin9XLocal) != 0) return WBEM_S_NO_ERROR;
// Short-circuit case: We get the __NAMESPACE and see if it
// the same as the NS in which we are executing the query.
// ========================================================
try // native interfaces throws
{ LPWSTR pszNamespace = m_pNs->GetName();
CVar vNs, vServer; if (FAILED(pObj->GetProperty(L"__NAMESPACE" , &vNs)) ||vNs.IsNull()) return WBEM_E_INVALID_OBJECT; if (FAILED(pObj->GetProperty(L"__SERVER", &vServer)) || vServer.IsNull()) return WBEM_E_INVALID_OBJECT;
// If server name and namespace are the same, we are already implicitly
// allowed to see the object.
// ====================================================================
if (wbem_wcsicmp(LPWSTR(vNs), pszNamespace) == 0 && wbem_wcsicmp(LPWSTR(vServer), ConfigMgr::GetMachineName()) == 0) return WBEM_S_NO_ERROR; } catch (CX_MemoryException &) { return WBEM_E_OUT_OF_MEMORY; } catch (CX_Exception &) { return WBEM_E_CRITICAL_ERROR; }
// If here, we have to do a real check.
// ====================================
HRESULT hRes = WBEM_S_NO_ERROR;
BOOL bRet = TRUE; CVar vProp; if (FAILED(pObj->GetProperty(L"__Path" , &vProp)) || vProp.IsNull()) return WBEM_E_INVALID_OBJECT;
// Parse the object path to get the class involved.
// ================================================
CObjectPathParser p; ParsedObjectPath* pOutput = 0;
int nStatus = p.Parse(vProp.GetLPWSTR(), &pOutput);
if (CObjectPathParser:: NoError != nStatus) return WBEM_E_OUT_OF_MEMORY;
OnDeleteObj<ParsedObjectPath*,CObjectPathParser, void (CObjectPathParser:: *)(ParsedObjectPath *), &CObjectPathParser::Free> FreeMe(&p,pOutput);
if (pOutput->IsLocal(ConfigMgr::GetMachineName())) { LPWSTR wszNewNamespace = pOutput->GetNamespacePart(); CDeleteMe<WCHAR> dm1(wszNewNamespace);
if (NULL == wszNewNamespace) return WBEM_E_OUT_OF_MEMORY;
if (wbem_wcsicmp(wszNewNamespace, m_pNs->GetName())) { CWbemNamespace* pNewLocal = CWbemNamespace::CreateInstance(); if (NULL == pNewLocal) return WBEM_E_OUT_OF_MEMORY; CReleaseMe rmNS((IWbemServices *)pNewLocal);
hRes = pNewLocal->Initialize(wszNewNamespace, m_pNs->GetUserName(), 0, 0, m_pNs->IsForClient(), TRUE, m_pNs->GetClientMachine(), m_pNs->GetClientProcID(), FALSE, NULL); if (SUCCEEDED(hRes)) { DWORD dwAccess = pNewLocal->GetUserAccess(); if ((dwAccess & WBEM_ENABLE) == 0) hRes = WBEM_E_ACCESS_DENIED;; } } }
return hRes; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// END HELPER FUNCTIONS
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// BEGIN SINK CODE
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//***************************************************************************
//
// CAssocQuery::CreateSink
//
// Creates a sink which is bound this the current query.
//
//***************************************************************************
//
CObjectSink *CAssocQuery::CreateSink( PF_FilterForwarder pfnFilter, BSTR strTrackingQuery ) { CAssocQE_Sink *p = new CAssocQE_Sink(this, pfnFilter, strTrackingQuery); if (p) p->AddRef(); return p; }
//***************************************************************************
//
// CAssocQE_Sink::CAssocQE_Sink
//
//***************************************************************************
//
CAssocQE_Sink::CAssocQE_Sink( CAssocQuery *pQuery, PF_FilterForwarder pFilter, BSTR strTrackingQuery ) : CObjectSink(0) // Starting ref count
{ m_pQuery = pQuery; m_pQuery->AddRef(); m_pfnFilter = pFilter; m_lRef = 0; m_bQECanceled = FALSE; m_bOriginalOpCanceled = FALSE; InterlockedIncrement(&m_pQuery->m_lActiveSinks); if (strTrackingQuery) m_strQuery = SysAllocString(strTrackingQuery);
}
//***************************************************************************
//
// CAssocQE_Sink::~CAssocQE_Sink
//
//***************************************************************************
// ok
CAssocQE_Sink::~CAssocQE_Sink() { InterlockedDecrement(&m_pQuery->m_lActiveSinks); m_pQuery->SignalSinkDone(); m_pQuery->Release(); SysFreeString(m_strQuery); }
//***************************************************************************
//
// CAssocQE_Sink::Indicate
//
//***************************************************************************
// ok
STDMETHODIMP CAssocQE_Sink::Indicate( IN long lNumObjects, IN IWbemClassObject** apObj ) { HRESULT hRes = WBEM_S_NO_ERROR;
if (ConfigMgr::ShutdownInProgress()) return WBEM_E_SHUTTING_DOWN;
// Short-circuit a cancelled sink.
// ===============================
if (m_bQECanceled) { return hRes; }
for (int i = 0; i < lNumObjects; i++) { IWbemClassObject *pCandidate = apObj[i];
if (m_pfnFilter) { // Call the filter & forward function bound to this
// sink instance.
hRes = (m_pQuery->*m_pfnFilter)(pCandidate);
// Check for out-and-out failure.
// ==============================
if (FAILED(hRes)) { m_bQECanceled = TRUE; m_pQuery->Cancel(); break; }
// If we are simply cancelling this one sink due to efficiency
// reasons, then tell just the provider to cancel, but not the
// whole query.
// ============================================================
if (hRes == WBEM_S_OPERATION_CANCELLED) { m_bQECanceled = TRUE; hRes = WBEM_E_CALL_CANCELLED; break; } } }
m_pQuery->UpdateTime(); return hRes; }
HRESULT CAssocQE_Sink::Add(IWbemClassObject* pObj) { return Indicate(1, &pObj); }
//***************************************************************************
//
// CAssocQE_Sink::SetStatus
//
//***************************************************************************
//
STDMETHODIMP CAssocQE_Sink::SetStatus( long lFlags, long lParam, BSTR strParam, IWbemClassObject* pObjParam ) { m_pQuery->UpdateTime(); m_pQuery->SignalSinkDone();
if (FAILED(lParam)) { // TBD report provider error; cancel query
} return WBEM_S_NO_ERROR; };
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// END SINK CODE
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// BEGIN CLASSDEFSONLY CODE
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//***************************************************************************
//
// GetClassDefsOnlyClass
//
// This takes an association instance, and looks up its class definition.
// It looks up the parent-most class possible which is non-abstract
// and instantiable. This class is already in the master class list.
//
// It then tags the roles on that class definition with IN or OUT depending
// on which property actually references the endpoint and which ones do
// not.
//
// Second, it does 'hypothetical' tagging, where the OUT roles are each
// given an independent pass to see if they *could* reference the
// query endpoint, and the IN role is examined to see if it could
// in turn reference the unknowns.
//
// Returns
// WBEM_E_INVALID_OBJECT if the association cannot point to
// the endpoint in the current query.
//
// WBEM_S_NO_ERROR is returned if IN/OUT tagging was properly
// achieved.
//
// WBEM_E_* on other conditions, which indicate drastic failure, such
// as out-of-memory.
//
//***************************************************************************
//
HRESULT CAssocQuery::GetClassDefsOnlyClass( IN IWbemClassObject *pExample, OUT IWbemClassObject **pClass ) { if (!pExample || !pClass) return WBEM_E_INVALID_PARAMETER;
*pClass = 0; // Get the class that we need.
// ===========================
HRESULT hRes; IWbemClassObject *pCandidate = 0; hRes = FindParentmostClass(pExample, &pCandidate); if (FAILED(hRes)) return hRes; CReleaseMe _(pCandidate);
_variant_t vClassName; hRes = pCandidate->Get(L"__CLASS", 0, &vClassName, 0, 0); if (FAILED(hRes) || V_VT(&vClassName) != VT_BSTR) return WBEM_E_FAILED;
// If the class has already been delivered, just quit now.
// =======================================================
for (int i = 0; i < m_aDeliveredClasses.Size(); i++) { if (wbem_wcsicmp(m_aDeliveredClasses[i], V_BSTR(&vClassName)) == 0) return WBEM_S_NO_ERROR; }
// If here, it's a new class. Make a copy that we can modify
// and send back to the user.
// ==========================================================
IWbemClassObject *pCopy = 0; hRes = pCandidate->Clone(&pCopy); if (FAILED(hRes)) return hRes; CReleaseMe rmCopy(pCopy);
hRes = ComputeInOutTags(pExample, pCopy); if (FAILED(hRes)) return hRes;
// Add the class name to the 'delivered' list.
// ===========================================
if (CFlexArray::no_error != m_aDeliveredClasses.Add(V_BSTR(&vClassName))) return WBEM_E_OUT_OF_MEMORY;
// Send it back. The additional AddRef is because of the
// CReleaseMe binding.
// ======================================================
*pClass = (IWbemClassObject *)rmCopy.dismiss();
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::TagProp
//
// Tags a property in an object with the named qualifier. Used to
// add IN or OUT to class definitions when executing CLASSDEFSONLY queries.
//
//***************************************************************************
//
HRESULT CAssocQuery::TagProp( IN IWbemClassObject *pObjToTag, IN LPCWSTR pszPropName, IN LPCWSTR pszInOutTag ) { IWbemQualifierSet *pQSet = 0; HRESULT hRes = pObjToTag->GetPropertyQualifierSet(pszPropName, &pQSet); if (FAILED(hRes)) return hRes; CReleaseMe _1(pQSet);
CVARIANT v; v.SetBool(TRUE); pQSet->Put(pszInOutTag, &v, 0);
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::ComputeInOutTags
//
// Computes the IN/OUT tags on a class using the specified association
// instance.
//
// Does not deliver the instance to the sink.
//
//***************************************************************************
// ok
HRESULT CAssocQuery::ComputeInOutTags( IN IWbemClassObject *pAssocInst, IN IWbemClassObject *pClass ) { HRESULT hRes;
if (pAssocInst == 0 || pClass == 0) return WBEM_E_INVALID_PARAMETER;
try { // Loop through the properties trying to find a legitimate
// reference to our endpoint class.
// =======================================================
pAssocInst->BeginEnumeration(WBEM_FLAG_REFS_ONLY); while (1) { BSTR strPropName = 0; hRes = pAssocInst->Next(0,&strPropName,0,0,0); CSysFreeMe _1(strPropName); if (hRes == WBEM_S_NO_MORE_DATA) break;
hRes = RoleTest(m_pEndpoint, pAssocInst, m_pNs, strPropName, ROLETEST_MODE_PATH_VALUE); if (SUCCEEDED(hRes)) { TagProp(pClass, strPropName, L"IN"); } else TagProp(pClass, strPropName, L"OUT"); } // Enum of ref properties
pAssocInst->EndEnumeration();
// Try to infer additional IN/OUT flows by examining the
// class itself. Some of these are only possible, rather
// the definite. Note that if more than one property
// has IN flow, {P1=IN, P2=IN, P3=OUT } then by implication each
// of P1 and P2 can also be OUT, since when one of (P1,P2) is IN
// the other must be OUT unless there are two refecences to
// the same object. Obviously, this entire mechanism is weak
// theoretically. It is only there for the CIM Object Browser
// to have some good idea that there are 'probably' instances
// for that particular association.
// =============================================================
CWStringArray aClassInProps; hRes = CanClassRefQueryEp(FALSE, pClass, &aClassInProps);
for (int i = 0; i < aClassInProps.Size(); i++) { TagProp(pClass, aClassInProps[i], L"IN"); for (int i2 = 0; i2 < aClassInProps.Size(); i2++) { // Tag all the others as OUTs as well.
if (wbem_wcsicmp(aClassInProps[i2], aClassInProps[i]) != 0) { TagProp(pClass, aClassInProps[i], L"OUT"); } } } } catch (CX_MemoryException &) // WString throws
{ return WBEM_E_FAILED; }
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::FindParentMostClass
//
// Finds the parent-most class definition object which is still a 'real'
// class, of the class name specified. Given {A,B:A,C:B,D:C}, all of
// which are instantiable, finds 'A' if 'D' is specified in the <pszClass>
// parameter.
//
// Note that the master class list only contains classes from the
// dynamic portion of the database. Thus, if the association is a static
// type, we simply look up the first non-abstract class in the repository.
//
//***************************************************************************
//
HRESULT CAssocQuery::FindParentmostClass( IN IWbemClassObject *pAssocInst, OUT IWbemClassObject **pClassDef ) { HRESULT hRes; int i;
if (pAssocInst == 0 || pClassDef == 0) return WBEM_E_INVALID_PARAMETER; *pClassDef = 0;
// Get the class hierarchy of the object.
// ======================================
CWStringArray aHierarchy; hRes = St_GetObjectInfo( pAssocInst, 0, 0, 0, aHierarchy );
if (FAILED(hRes)) return hRes;
IWbemClassObject *pTarget = 0;
// Traverse the hierarchy, looking for the class def.
// ==================================================
for (i = aHierarchy.Size() - 1; i >= 0; i--) { for (int i2 = 0; i2 < m_aMaster.Size(); i2++) { IWbemClassObject *pObj = (IWbemClassObject *) m_aMaster[i2]; CVARIANT vClassName; hRes = pObj->Get(L"__CLASS", 0, &vClassName, 0, 0); if (FAILED(hRes) || vClassName.GetType() != VT_BSTR) return WBEM_E_FAILED;
if (wbem_wcsicmp(aHierarchy[i], vClassName.GetStr()) == 0) { pTarget = pObj; break; } } if (pTarget) break; }
// If the association class was non-dynamic, it won't have been located
// by the above search. Instead, we will go to the repository and
// starting with the dynasty superclass, work down to the current class
// until we find a non-abstract class.
// ====================================================================
if (pTarget == 0) { for (i = aHierarchy.Size() - 1; i >= 0; i--) { IWbemClassObject *pTest = 0; hRes = Db_GetClass(aHierarchy[i], &pTest); if (FAILED(hRes)) break; hRes = St_ObjHasQualifier(L"ABSTRACT", pTest); if (SUCCEEDED(hRes)) { pTest->Release(); continue; } else // This is what we want to send back
{ *pClassDef = pTest; return WBEM_S_NO_ERROR; } } }
// Now, see if we found it.
// ========================
if (pTarget == 0) return WBEM_E_NOT_FOUND;
pTarget->AddRef(); *pClassDef = pTarget; return WBEM_S_NO_ERROR; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// END CLASSDEFSONLY CODE
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// BEGIN SCHEMA-ONLY SPECIFIC CODE
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//***************************************************************************
//
// CAssocQuery::SchemaQ_RefsFilter
//
// Reduces the class set of a schemaonly 'references of' query by
// cutting out anything specified in the filters. The filters applied
// are RESULTCLASS, REQUIREDQUALIFIER, and ROLE.
//
// The size and content of <aSrc> is altered. Objects not used
// are Released().
//
// Returns status in HRESULT, does not access the destination sink on error.
//
//***************************************************************************
// executions=1; no filtering though
HRESULT CAssocQuery::SchemaQ_RefsFilter( IN OUT CFlexArray &aSrc // IN: the unreduced class set, OUT the reduced one
) { HRESULT hRes;
// Loop through the result set, looking for things to toss out.
// ============================================================
for (int i = 0; i < aSrc.Size(); i++) // x
{ BOOL bIsACandidate = TRUE;
// Extract this class definition from the source array.
// ====================================================
IWbemClassObject *pCls = (IWbemClassObject *) aSrc[i];
// Start testing.
//
// RESULTCLASS --the object must be of the specified
// class or part of its hierarchy.
// ==================================================
LPCWSTR pszResultClass = m_Parser.GetResultClass(); if (pszResultClass) { hRes = St_ObjIsOfClass(pszResultClass, pCls); if (FAILED(hRes)) { aSrc[i] = 0; pCls->Release(); hRes = 0; continue; } }
// If here, there either isn't a RESULTCLASS test or we passed it.
// Next, we try REQUIREDQUALIFIER.
// ===============================================================
LPCWSTR pszRequiredQual = m_Parser.GetRequiredQual(); if (pszRequiredQual) { hRes = St_ObjHasQualifier(pszRequiredQual, pCls); if (FAILED(hRes)) { aSrc[i] = 0; pCls->Release(); hRes = 0; continue; } }
// Next, we try ROLE.
// ==================
LPCWSTR pszRole = m_Parser.GetRole(); // x
if (pszRole) { hRes = RoleTest(m_pEndpoint, pCls, m_pNs, pszRole, ROLETEST_MODE_CIMREF_TYPE); if (FAILED(hRes)) { aSrc[i] = 0; pCls->Release(); hRes = 0; continue; } }
}
aSrc.Compress();
return WBEM_S_NO_ERROR; }
//****************************************************************************
//
// CAssocQuery::TerminateSchemaQuery
//
// For schema queries, sends the final result objects to the destination
// sink and shuts down the query. At this point, all the objects are in
// the result set array and ready to be delivered.
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::SchemaQ_Terminate( IN CFlexArray &aResultSet ) { HRESULT hRes = WBEM_S_NO_ERROR;
aResultSet.Compress(); // Remove NULLs
// Indicate everything.
// ====================
if (aResultSet.Size()) { IWbemClassObject **p = (IWbemClassObject **) aResultSet.GetArrayPtr(); hRes = m_pDestSink->Indicate(aResultSet.Size(), p); St_ReleaseArray(aResultSet); }
return hRes; }
//****************************************************************************
//
// CAssocQuery::SchemaQ_RefsQuery
//
// At this point we have the final list of classes. We now apply any
// secondary filters and send the result back to the client.
//
// (1) We apply all filters specified in the query.
// (2) If CLASSDEFSONLY, we post filter yet again.
// (3) Deliver to client
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::SchemaQ_RefsQuery( IN OUT CFlexArray &aResultSet ) { HRESULT hRes;
// Apply various filters.
// ======================
hRes = SchemaQ_RefsFilter(aResultSet); if (FAILED(hRes)) return hRes;
return SchemaQ_Terminate(aResultSet); }
//****************************************************************************
//
// CAssocQuery::SchemaQ_AssocsQuery
//
// At this point we have the list of association classes. We apply
// association-level filters, and then get the other endpoint classes,
// filtering them in parallel. The final result set is placed in
// <aOtherEndpoints> and delivered to the user by the final call
// to SchemaQ_Terminate.
//
//****************************************************************************
// visual ok
HRESULT CAssocQuery::SchemaQ_AssocsQuery( IN CFlexArray &aAssocSet ) { HRESULT hRes;
// Apply association filters.
// ========================
hRes = SchemaQ_AssocsFilter(aAssocSet); if (FAILED(hRes)) return hRes;
// Now, get the other endpoints. We filter them
// in parallel, due to the good locality of reference
// in this case.
// ==================================================
CFlexArray aOtherEndpoints;
hRes = SchemaQ_GetAndFilterOtherEndpoints( aAssocSet, aOtherEndpoints );
St_ReleaseArray(aAssocSet); // Done with the associations themselves
if (FAILED(hRes)) return hRes;
// Apply other-endpoint filters.
// =============================
return SchemaQ_Terminate(aOtherEndpoints); }
//***************************************************************************
//
// CAssocQuery::ConvertEpListToClassDefsOnly
//
// Filters the endpoint list of instances and changes it into the minimal
// set of class definitions. Classes must be in the same namespace.
//
//***************************************************************************
//
HRESULT CAssocQuery::ConvertEpListToClassDefsOnly() { CFlexArray aNew; HRESULT hRes; BOOL bArrayNeedsCleanup = FALSE;
CInCritSec ics(&m_csCandidateEpAccess);
for (int i = 0; i < m_aEpCandidates.Size(); i++) { BSTR strEpPath = (BSTR) m_aEpCandidates[i]; if (strEpPath == 0) continue;
BSTR strClassName = 0; hRes = St_ObjPathInfo(strEpPath, &strClassName, 0); if (FAILED(hRes)) { hRes = 0; continue; }
BOOL bFound = FALSE;
// See if class is in our new destination array.
// =============================================
for (int i2 = 0; i2 < aNew.Size(); i2++) { BSTR strTest = (BSTR) aNew[i2]; if (wbem_wcsicmp(strClassName, strTest) == 0) { bFound = TRUE; break; } }
if (bFound == TRUE) SysFreeString(strClassName); else { if (CFlexArray::no_error != aNew.Add(strClassName)) { SysFreeString(strClassName); bArrayNeedsCleanup = TRUE; break; } } }
if (bArrayNeedsCleanup) { for (i = 0; i < aNew.Size(); i++) SysFreeString((BSTR)aNew[i]); return WBEM_E_OUT_OF_MEMORY; }
EmptyCandidateEpArray();
for (i = 0; i < aNew.Size(); i++) { if (CFlexArray::no_error != m_aEpCandidates.Add(aNew[i])) { bArrayNeedsCleanup = TRUE; break; } }
if (bArrayNeedsCleanup) { // continue from where you finished
for (; i < aNew.Size(); i++) SysFreeString((BSTR)aNew[i]); return WBEM_E_OUT_OF_MEMORY; }
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::SchemaQ_AssocsFilter
//
// Called during an 'associators of' query, this filters out the
// classes which don't pass the test for the association classes
// themselves.
//
// Tests for ROLE and REQUIREDASSOCQUALIFIER.
//
//***************************************************************************
// ok
HRESULT CAssocQuery::SchemaQ_AssocsFilter( IN OUT CFlexArray &aSrc ) { HRESULT hRes;
LPCWSTR pszRole = m_Parser.GetRole(); LPCWSTR pszRequiredAssocQual = m_Parser.GetRequiredAssocQual();
// If there are no filters anyway, short-circuit.
// ==============================================
if (pszRole == 0 && pszRequiredAssocQual == 0) { return WBEM_S_NO_ERROR; }
// If here, some tests are required.
// =================================
for (int i = 0; i < aSrc.Size(); i++) { IWbemClassObject *pCls = (IWbemClassObject *) aSrc[i];
// If ROLE is present, ensure query endpoint is referenced
// by it.
// =======================================================
if (pszRole) { hRes = RoleTest(m_pEndpoint, pCls, m_pNs, pszRole, ROLETEST_MODE_CIMREF_TYPE); if (FAILED(hRes)) { aSrc[i] = 0; pCls->Release(); hRes = 0; continue; } }
// If REQUIREDASSOCQUALIFIER was in the query,
// ensure it is present.
// ===========================================
if (pszRequiredAssocQual) { hRes = St_ObjHasQualifier(pszRequiredAssocQual, pCls); if (FAILED(hRes)) { aSrc[i] = 0; pCls->Release(); hRes = 0; continue; } } }
aSrc.Compress();
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::SchemaQ_GetAndFilterOtherEndpoints
//
// Given the set of classes in <aAssocs>, get the other endpoint
// classes.
//
// The filtering is achieved in parallel, since we have locality
// of reference between the association object and the endpoint.
//
// Parameters:
// <aAssocs> The association classes.
// <aEndpoints> Receives the endpoint classes.
//
// Result:
// HRESULT Does not access the destination sink.
//
//***************************************************************************
// ok
HRESULT CAssocQuery::SchemaQ_GetAndFilterOtherEndpoints( IN CFlexArray &aAssocs, OUT CFlexArray &aEndpoints ) { HRESULT hRes;
for (int i = 0; i < aAssocs.Size(); i++) { IWbemClassObject *pAssoc = (IWbemClassObject *) aAssocs[i]; IWbemClassObject *pEpClass = 0;
// Find the property that references the other endpoint.
// ====================================================
BSTR strOtherEpName = 0; hRes = SchemaQ_GetOtherEpClassName(pAssoc, &strOtherEpName); if (FAILED(hRes)) continue;
CSysFreeMe _1(strOtherEpName);
// If we failed to get a name we should continue.
if ( S_OK != hRes ) { hRes = 0; continue; }
// Now, get that class. The class comes back
// property AddRef'ed. If we don't use it, then we
// have to Release it.
// ===============================================
hRes = GetClassFromAnywhere(strOtherEpName, 0, &pEpClass);
if (FAILED(hRes)) { // WE have a dangling reference.
// =============================
ERRORTRACE((LOG_WBEMCORE, "Invalid path %S specified in an association class\n", strOtherEpName)); EmptyObjectList(aEndpoints); return WBEM_E_INVALID_OBJECT_PATH; }
//
// If here, we have the endpoint class in <pEpClass>
// and the associationclass in pAssoc.
// Now, apply the filters, both to the association and the endpoint.
//
// RESULTCLASS
// Verify that the class of the endpoint is this
// or part of its hierarchy.
// =============================================
LPCWSTR pszResultClass = m_Parser.GetResultClass(); if (pszResultClass) { hRes = St_ObjIsOfClass(pszResultClass, pEpClass); if (FAILED(hRes)) { pEpClass->Release(); hRes = 0; continue; } }
// ROLE.
// The association must point back to the endpoint
// via this.
// ================================================
LPCWSTR pszRole = m_Parser.GetRole(); if (pszRole) { hRes = RoleTest(m_pEndpoint, pAssoc, m_pNs, pszRole, ROLETEST_MODE_CIMREF_TYPE); if (FAILED(hRes)) { pEpClass->Release(); hRes = 0; continue; } }
// RESULTROLE
// The association must point to the other endpoint
// via this property.
// ================================================
LPCWSTR pszResultRole = m_Parser.GetResultRole(); if (pszResultRole) { hRes = RoleTest(pEpClass, pAssoc, m_pNs, pszResultRole, ROLETEST_MODE_CIMREF_TYPE); if (FAILED(hRes)) { pEpClass->Release(); hRes = 0; continue; } }
// ASSOCCLASS
// Verify that the class of the association is this.
// =================================================
LPCWSTR pszAssocClass = m_Parser.GetAssocClass(); if (pszAssocClass) { hRes = St_ObjIsOfClass(pszAssocClass, pAssoc); if (FAILED(hRes)) { pEpClass->Release(); hRes = 0; continue; } }
// REQUIREDQUALIFIER
// Endpoint must have this qualifier.
// ===================================
LPCWSTR pszQual = m_Parser.GetRequiredQual(); if (pszQual) { hRes = St_ObjHasQualifier(pszQual, pEpClass); if (FAILED(hRes)) { pEpClass->Release(); hRes = 0; continue; } }
// REQUIREDASSOCQUALIFIER
// Association object must have this qualifier.
// ============================================
LPCWSTR pszRequiredAssocQual = m_Parser.GetRequiredAssocQual(); if (pszRequiredAssocQual) { hRes = St_ObjHasQualifier(pszRequiredAssocQual, pAssoc); if (FAILED(hRes)) { pEpClass->Release(); hRes = 0; continue; } }
// If here, we passed the barrage of filtering
// tests and can happily report that the class
// is part of the result set.
// ===========================================
if (CFlexArray::no_error != aEndpoints.Add(pEpClass)) { pEpClass->Release(); return WBEM_E_OUT_OF_MEMORY; } }
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
// CAssocQuery::SchemaQ_GetOtherEpClassName
//
// Finds the property in the association which references the
// 'other endpoint' in the query. This is achieved by locating
// a property which *does* reference the endpoint and assuming that
// any remaining property must reference the 'other endpoint'.
// If both references can reach the query endpoint, then no
// harm is done
//
// This function assumes well-formed associations with two
// references.
//
// PARAMETERS:
// <pAssoc> The association class
// <strOtherEpName> Receives the name of the class of the 'other endpoint'
//
// RESULT:
// WBEM_S_NO_ERROR, WBEM_E_FAILED
//
//***************************************************************************
// ok
HRESULT CAssocQuery::SchemaQ_GetOtherEpClassName( IN IWbemClassObject *pAssocClass, OUT BSTR *strOtherEpName ) { HRESULT hRes = WBEM_E_FAILED;
if (strOtherEpName == 0) return hRes; *strOtherEpName = 0;
BOOL bStrict = (m_Parser.GetQueryType() & QUERY_TYPE_SCHEMA_ONLY) != 0;
// Enumerate just the references.
// ===============================
hRes = pAssocClass->BeginEnumeration(WBEM_FLAG_REFS_ONLY); if (FAILED(hRes)) return WBEM_E_FAILED;
// Loop through the references.
// ============================
int nCount = 0; while (1) { CVARIANT vRefPath; BSTR strPropName = 0; BSTR strEpClass = 0;
hRes = pAssocClass->Next( 0, // Flags
&strPropName, // Name
vRefPath, // Value
0, // CIM type (refs only already)
0 // Flavor
);
if (hRes == WBEM_S_NO_MORE_DATA) break;
CSysFreeMe _1(strPropName); hRes = CanPropRefQueryEp(bStrict, strPropName, pAssocClass, &strEpClass); CSysFreeMe _2(strEpClass);
if (FAILED(hRes) || nCount) { // If here on the second iteration or the first iteration
// with a failure, we have found the 'other endpoint'.
// ======================================================
*strOtherEpName = SysAllocString(strEpClass); if (*strOtherEpName == 0) return WBEM_E_OUT_OF_MEMORY; hRes = WBEM_S_NO_ERROR; break; } else nCount++; }
pAssocClass->EndEnumeration();
return hRes; }
//***************************************************************************
//
// CAssocQuery::CanPropRefQueryEp
//
// For class definitions, determines if the specified property in the
// object can reference the query endpoint. This works for both strongly
// typed and CLASSREF typed properties.
//
// PARAMETERS:
// <pszPropName> The property to test. Must be a reference property.
// <bStrict> If TRUE, then the property must actually reference
// the class of the endpoint. If FALSE, it may reference
// any of the superclasses of the endpoint.
// <pObj> The association object with the property to be tested.
// <strRefType> Optionally receives the name of the class in the
// CIMTYPE "REF:Classname>" string, as long as
// the reference is strongly typed (does not work
// for CLASSREF types).
//
// RETURNS:
// HRESULT
// WBEM_S_NO_ERROR if the property can reference the query endpoint.
// WBEM_E_NOT_FOUND if the property cannot reference the query endpoint.
// or
// WBEM_E_INVALID_PARAMETER
// WBEM_E_FAILED
//
//***************************************************************************
//
HRESULT CAssocQuery::CanPropRefQueryEp( IN BOOL bStrict, IN LPWSTR pszPropName, IN IWbemClassObject *pObj, OUT BSTR *strRefType ) { HRESULT hRes; wchar_t ClassName[MAX_CLASS_NAME];
*ClassName = 0;
if (pszPropName == 0 || pObj == 0) return WBEM_E_INVALID_PARAMETER;
// Get the qualifier set for this property.
// ========================================
IWbemQualifierSet *pQSet = 0; hRes = pObj->GetPropertyQualifierSet(pszPropName,&pQSet); if (FAILED(hRes)) return WBEM_E_FAILED; CReleaseMe _1(pQSet);
// Now get the CIMTYPE of this reference.
// ======================================
CVARIANT v; hRes = pQSet->Get(L"CIMTYPE", 0, &v, 0); if (FAILED(hRes) || V_VT(&v) != VT_BSTR) return WBEM_E_FAILED;
BSTR strRefClass = V_BSTR(&v); if (strRefClass) { if (wcslen_max(strRefClass,MAX_CLASS_NAME) > MAX_CLASS_NAME) return WBEM_E_FAILED; parse_REF(strRefClass,MAX_CLASS_NAME,ClassName); }
// Send a copy of the class name back to the
// caller, if required.
// =========================================
if (strRefType) { *strRefType = 0; if (*ClassName) { *strRefType = SysAllocString(ClassName); if (*strRefType == 0) return WBEM_E_OUT_OF_MEMORY; } }
// Now see if this class is any of the classes in our
// query endpoint.
// ==================================================
if (*ClassName) { // If <bStrict> we must match the class name of the
// query endpoint exactly.
if (bStrict) { if (wbem_wcsicmp(ClassName, m_bstrEndpointClass) == 0) return WBEM_S_NO_ERROR; } // Else, any of the superclasses of the endpoint will do.
else { for (int i = 0; i < m_aEndpointHierarchy.Size(); i++) { if (wbem_wcsicmp(ClassName, m_aEndpointHierarchy[i]) == 0) return WBEM_S_NO_ERROR; } } }
// If here, we can try to see if the property has a CLASSREF
// qualifier instead.
// =========================================================
hRes = CanClassRefReachQueryEp(pQSet, bStrict);
if (SUCCEEDED(hRes)) return WBEM_S_NO_ERROR;
// If here, the property doesn't reference the query
// endpoint in any way.
// =================================================
return WBEM_E_NOT_FOUND; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// END SCHEMA-ONLY SPECIFIC CODE
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// BEGIN DYNAMIC CLASS HELPERS
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//***************************************************************************
//
// ClassNameTest
//
// Sort helper
//
//***************************************************************************
//
static int ClassNameTest( IN CFlexArray &Classes, IN int nIndex1, // iBackscan
IN int nIndex2 // iBackscan-nInterval
) { HRESULT hr; // Name test.
IWbemClassObject *pC1 = (IWbemClassObject *) Classes[nIndex1]; IWbemClassObject *pC2 = (IWbemClassObject *) Classes[nIndex2];
CVARIANT v1, v2; hr = pC1->Get(L"__CLASS", 0, &v1, 0, 0); if (FAILED(hr) || VT_BSTR != V_VT(&v1)) return 1; hr = pC2->Get(L"__CLASS", 0, &v2, 0, 0); if (FAILED(hr) || VT_BSTR != V_VT(&v2)) return 1; return wbem_wcsicmp(V_BSTR(&v1), V_BSTR(&v2)); }
//***************************************************************************
//
// CAssocQuery::SortDynClasses
//
// Sorts the dynamic classes so they can be binary searched later.
//
//***************************************************************************
//
void CAssocQuery::SortDynClasses() { // Shell sort.
// ===========
int nSize = m_aDynClasses.Size();
for (int nInterval = 1; nInterval < nSize / 9; nInterval = nInterval * 3 + 1);
while (nInterval) { for (int iCursor = nInterval; iCursor < nSize; iCursor++) { int iBackscan = iCursor;
while (iBackscan - nInterval >= 0 && ClassNameTest(m_aDynClasses, iBackscan, iBackscan-nInterval) < 0) { // Swap.
// =====
IWbemClassObject *pTmp = (IWbemClassObject *) m_aDynClasses[iBackscan - nInterval]; m_aDynClasses[iBackscan - nInterval] = m_aDynClasses[iBackscan]; m_aDynClasses[iBackscan] = pTmp; iBackscan -= nInterval; } } nInterval /= 3; } }
//***************************************************************************
//
// CAssocQuery::GetDynClasses
//
// Fills the per-query cache with all available dynamic assoc classes.
//
//***************************************************************************
//
HRESULT CAssocQuery::GetDynClasses() {
CSynchronousSink* pDynClassSink = 0; HRESULT hRes = 0;
// Now, get all dynamic classes.
// =============================
pDynClassSink = CSynchronousSink::Create(); if (NULL == pDynClassSink) return WBEM_E_OUT_OF_MEMORY; pDynClassSink->AddRef(); CReleaseMe _1(pDynClassSink);
hRes = m_pNs->GetDynamicReferenceClasses( 0L, m_pContext, pDynClassSink ); if (FAILED(hRes)) return hRes;
pDynClassSink->Block(); pDynClassSink->GetStatus(&hRes, NULL, NULL);
// Now get all the dynamic class definitions.
CRefedPointerArray<IWbemClassObject>& raObjects = pDynClassSink->GetObjects(); for (int i = 0; i < raObjects.GetSize(); i++) { IWbemClassObject *pClsDef = (IWbemClassObject *) raObjects[i]; if (CFlexArray::no_error == m_aDynClasses.Add(pClsDef)) { pClsDef->AddRef(); } }
SortDynClasses();
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
// CAssocQuery::GetDynClass
//
// Attempts to find the requested class in the dynamic class cache.
//
//***************************************************************************
//
HRESULT CAssocQuery::GetDynClass( IN LPCWSTR pszClassName, OUT IWbemClassObject **pCls ) { HRESULT hRes; if (pCls == 0 || pszClassName == 0) return WBEM_E_INVALID_PARAMETER; *pCls = 0;
CFlexArray &a = m_aDynClasses;
// Binary search the cache.
// ========================
int l = 0, u = a.Size() - 1; while (l <= u) { int m = (l + u) / 2; IWbemClassObject *pItem = (IWbemClassObject *) a[m];
CVARIANT vClassName; hRes = pItem->Get(L"__CLASS", 0, &vClassName, 0, 0); if (FAILED(hRes) || VT_BSTR != V_VT(&vClassName)) return WBEM_E_NOT_FOUND; int nRes = wbem_wcsicmp(pszClassName, V_BSTR(&vClassName));
if (nRes < 0) u = m - 1; else if (nRes > 0) l = m + 1; else { pItem->AddRef(); *pCls = pItem; return WBEM_S_NO_ERROR; } }
return WBEM_E_NOT_FOUND; }
//***************************************************************************
//
// GetClassDynasty
//
// Gets all the classes in a dynasty. The returned array has a
// set of IWbemClassObject pointers that need releasing.
//
//***************************************************************************
//
HRESULT CAssocQuery::GetClassDynasty( IN LPCWSTR pszClass, OUT CFlexArray &aDynasty ) { HRESULT hRes; CSynchronousSink* pClassSink = CSynchronousSink::Create(); if (NULL == pClassSink) return WBEM_E_OUT_OF_MEMORY; pClassSink->AddRef(); CReleaseMe _1(pClassSink);
hRes = m_pNs->Exec_CreateClassEnum( LPWSTR(pszClass), WBEM_FLAG_DEEP, m_pContext, pClassSink);
if (FAILED(hRes)) return hRes; pClassSink->GetStatus(&hRes, NULL, NULL); if (FAILED(hRes)) return hRes;
aDynasty.Bind(pClassSink->GetObjects().GetArray()); return WBEM_S_NO_ERROR; }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//
// END DYNAMIC CLASS HELPERS
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|