/*++ 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 #include #include #include #include #include // // 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 . // // 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 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 or 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: // 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;jRelease(); } return hRes; } //**************************************************************************** // // CAssocQuery::CanClassRefQueryEp // // Determines if a class can reference the endpoint class. // // This works for both strongly typed and CLASSREF typed objects. // // PARAMETERS: // 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. // The class to test. // 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 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 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 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 // . 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""); 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 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 CleanMe(aTriads); if (FAILED(hRes = CoImpersonateClient())) return hRes; { OnDelete0 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""); 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""); 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 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 CleanMe(aTriads); if (FAILED(hRes = CoImpersonateClient())) return hRes; { OnDelete0 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""); 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 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. // // 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. // // bound to the property which supposedly references the query // endpoint. // 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 object can point to the object // via the specified property. // // Parameters: // The test endpoint object // The association object which may point to the endpoint. // The role to use for the test. // 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 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 and 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 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 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 _1(pszNormalizedPath); CDeleteMe _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 qualifier. // // Parameters // Points to the object to be tested (read-only). // // Return value: // WBEM_S_NO_ERROR If the class has a 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 FreeMe(&p,pOutput); if (pOutput->IsLocal(ConfigMgr::GetMachineName())) { LPWSTR wszNewNamespace = pOutput->GetNamespacePart(); CDeleteMe 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 // 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 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 // 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 , 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: // The association classes. // 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 // 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: // The association class // 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: // The property to test. Must be a reference property. // 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. // The association object with the property to be tested. // 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 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& 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 // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@