Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5188 lines
151 KiB

/*++
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
//
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@