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