//****************************************************************************** // // ANALYSER.CPP // // Copyright (C) 1996-1999 Microsoft Corporation // //****************************************************************************** #include "precomp.h" #include "pragmas.h" #include "analyser.h" #include #include #include #include #include #include #include "CWbemTime.h" #include CClassInfoArray::CClassInfoArray() : m_bLimited( FALSE ) { m_pClasses = new CUniquePointerArray; if ( m_pClasses ) { m_pClasses->RemoveAll(); } } CClassInfoArray::~CClassInfoArray() { delete m_pClasses; } bool CClassInfoArray::operator=(CClassInfoArray& Other) { SetLimited(Other.IsLimited()); m_pClasses->RemoveAll(); for(int i = 0; i < Other.m_pClasses->GetSize(); i++) { CClassInformation* pInfo = new CClassInformation(*(*Other.m_pClasses)[i]); if(pInfo == NULL) return false; m_pClasses->Add(pInfo); } return true; } bool CClassInfoArray::SetOne(LPCWSTR wszClass, BOOL bIncludeChildren) { CClassInformation* pNewInfo = _new CClassInformation; if(pNewInfo == NULL) return false; pNewInfo->m_wszClassName = CloneWstr(wszClass); if(pNewInfo->m_wszClassName == NULL) { delete pNewInfo; return false; } pNewInfo->m_bIncludeChildren = bIncludeChildren; m_pClasses->RemoveAll(); m_pClasses->Add(pNewInfo); SetLimited(TRUE); return true; } HRESULT CQueryAnalyser::GetPossibleInstanceClasses( QL_LEVEL_1_RPN_EXPRESSION* pExpr, CClassInfoArray*& paInfos) { // Organize a stack of classinfo arrays // ==================================== std::stack > > InfoStack; HRESULT hres = WBEM_S_NO_ERROR; // "Evaluate" the query // ==================== if(pExpr->nNumTokens == 0) { // Empty query --- no information // ============================== paInfos = _new CClassInfoArray; if(paInfos == NULL) return WBEM_E_OUT_OF_MEMORY; paInfos->SetLimited(FALSE); return WBEM_S_NO_ERROR; } for(int i = 0; i < pExpr->nNumTokens; i++) { QL_LEVEL_1_TOKEN& Token = pExpr->pArrayOfTokens[i]; CClassInfoArray* paNew = _new CClassInfoArray; if(paNew == NULL) return WBEM_E_OUT_OF_MEMORY; CClassInfoArray* paFirst; CClassInfoArray* paSecond; switch(Token.nTokenType) { case QL1_OP_EXPRESSION: hres = GetInstanceClasses(Token, *paNew); InfoStack.push(paNew); break; case QL1_AND: if(InfoStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } paFirst = InfoStack.top(); InfoStack.pop(); paSecond = InfoStack.top(); InfoStack.pop(); hres = AndPossibleClassArrays(paFirst, paSecond, paNew); InfoStack.push(paNew); delete paFirst; delete paSecond; break; case QL1_OR: if(InfoStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } paFirst = InfoStack.top(); InfoStack.pop(); paSecond = InfoStack.top(); InfoStack.pop(); hres = OrPossibleClassArrays(paFirst, paSecond, paNew); InfoStack.push(paNew); delete paFirst; delete paSecond; break; case QL1_NOT: if(InfoStack.size() < 1) { hres = WBEM_E_CRITICAL_ERROR; break; } paFirst = InfoStack.top(); InfoStack.pop(); hres = NegatePossibleClassArray(paFirst, paNew); InfoStack.push(paNew); delete paFirst; break; default: hres = WBEM_E_CRITICAL_ERROR; delete paNew; } if(FAILED(hres)) { // An error occurred, break out of the loop // ======================================== break; } } if(SUCCEEDED(hres) && InfoStack.size() != 1) { hres = WBEM_E_CRITICAL_ERROR; } if(FAILED(hres)) { // An error occurred. Clear the stack // ================================== while(!InfoStack.empty()) { delete InfoStack.top(); InfoStack.pop(); } return hres; } // All is good // =========== paInfos = InfoStack.top(); return S_OK; } HRESULT CQueryAnalyser::AndPossibleClassArrays(IN CClassInfoArray* paFirst, IN CClassInfoArray* paSecond, OUT CClassInfoArray* paNew) { // For now, simply pick one // ======================== if(paFirst->IsLimited()) *paNew = *paFirst; else *paNew = *paSecond; return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::OrPossibleClassArrays(IN CClassInfoArray* paFirst, IN CClassInfoArray* paSecond, OUT CClassInfoArray* paNew) { // Append them together // ==================== paNew->Clear(); if(paFirst->IsLimited() && paSecond->IsLimited()) { paNew->SetLimited(TRUE); for(int i = 0; i < paFirst->GetNumClasses(); i++) { CClassInformation* pInfo = new CClassInformation(*paFirst->GetClass(i)); if(pInfo == NULL) return WBEM_E_OUT_OF_MEMORY; if(!paNew->AddClass(pInfo)) { delete pInfo; return WBEM_E_OUT_OF_MEMORY; } } for(i = 0; i < paSecond->GetNumClasses(); i++) { CClassInformation* pInfo = new CClassInformation(*paSecond->GetClass(i)); if(pInfo == NULL) return WBEM_E_OUT_OF_MEMORY; if(!paNew->AddClass(pInfo)) { delete pInfo; return WBEM_E_OUT_OF_MEMORY; } } } return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::NegatePossibleClassArray(IN CClassInfoArray* paOrig, OUT CClassInfoArray* paNew) { // No information! // =============== paNew->Clear(); return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::GetDefiniteInstanceClasses( QL_LEVEL_1_RPN_EXPRESSION* pExpr, CClassInfoArray*& paInfos) { // Organize a stack of classinfo arrays // ==================================== std::stack > > InfoStack; HRESULT hres = WBEM_S_NO_ERROR; // "Evaluate" the query // ==================== if(pExpr->nNumTokens == 0) { // Empty query --- no information // ============================== paInfos = _new CClassInfoArray; if(paInfos == NULL) return WBEM_E_OUT_OF_MEMORY; paInfos->SetLimited(FALSE); return WBEM_S_NO_ERROR; } for(int i = 0; i < pExpr->nNumTokens; i++) { QL_LEVEL_1_TOKEN& Token = pExpr->pArrayOfTokens[i]; CClassInfoArray* paNew = _new CClassInfoArray; if(paNew == NULL) return WBEM_E_OUT_OF_MEMORY; CClassInfoArray* paFirst; CClassInfoArray* paSecond; switch(Token.nTokenType) { case QL1_OP_EXPRESSION: hres = GetInstanceClasses(Token, *paNew); InfoStack.push(paNew); break; case QL1_AND: if(InfoStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } paFirst = InfoStack.top(); InfoStack.pop(); paSecond = InfoStack.top(); InfoStack.pop(); hres = AndDefiniteClassArrays(paFirst, paSecond, paNew); InfoStack.push(paNew); delete paFirst; delete paSecond; break; case QL1_OR: if(InfoStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } paFirst = InfoStack.top(); InfoStack.pop(); paSecond = InfoStack.top(); InfoStack.pop(); hres = OrDefiniteClassArrays(paFirst, paSecond, paNew); InfoStack.push(paNew); delete paFirst; delete paSecond; break; case QL1_NOT: if(InfoStack.size() < 1) { hres = WBEM_E_CRITICAL_ERROR; break; } paFirst = InfoStack.top(); InfoStack.pop(); hres = NegateDefiniteClassArray(paFirst, paNew); InfoStack.push(paNew); delete paFirst; break; default: hres = WBEM_E_CRITICAL_ERROR; delete paNew; } if(FAILED(hres)) { // An error occurred, break out of the loop // ======================================== break; } } if(SUCCEEDED(hres) && InfoStack.size() != 1) { hres = WBEM_E_CRITICAL_ERROR; } if(FAILED(hres)) { // An error occurred. Clear the stack // ================================== while(!InfoStack.empty()) { delete InfoStack.top(); InfoStack.pop(); } return hres; } // All is good // =========== paInfos = InfoStack.top(); return S_OK; } HRESULT CQueryAnalyser::AndDefiniteClassArrays(IN CClassInfoArray* paFirst, IN CClassInfoArray* paSecond, OUT CClassInfoArray* paNew) { // Nothing is definite if both conditions have to hold // =================================================== paNew->Clear(); paNew->SetLimited(TRUE); return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::OrDefiniteClassArrays(IN CClassInfoArray* paFirst, IN CClassInfoArray* paSecond, OUT CClassInfoArray* paNew) { // Append them together // ==================== paNew->Clear(); if(paFirst->IsLimited() && paSecond->IsLimited()) { paNew->SetLimited(TRUE); for(int i = 0; i < paFirst->GetNumClasses(); i++) { CClassInformation* pInfo = new CClassInformation(*paFirst->GetClass(i)); if(pInfo == NULL) return WBEM_E_OUT_OF_MEMORY; if(!paNew->AddClass(pInfo)) { delete pInfo; return WBEM_E_OUT_OF_MEMORY; } } for(i = 0; i < paSecond->GetNumClasses(); i++) { CClassInformation* pInfo = new CClassInformation(*paSecond->GetClass(i)); if(pInfo == NULL) return WBEM_E_OUT_OF_MEMORY; if(!paNew->AddClass(pInfo)) { delete pInfo; return WBEM_E_OUT_OF_MEMORY; } } } return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::NegateDefiniteClassArray(IN CClassInfoArray* paOrig, OUT CClassInfoArray* paNew) { // No information // ============== paNew->Clear(); paNew->SetLimited(TRUE); return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::GetInstanceClasses(QL_LEVEL_1_TOKEN& Token, CClassInfoArray& aInfos) { // Preset aInfos to the "no information" value // =========================================== aInfos.Clear(); // See if this token talks about TargetInstance or PreviousInstance // ================================================================ if(Token.PropertyName.GetNumElements() < 1) return WBEM_S_NO_ERROR; LPCWSTR wszPrimaryName = Token.PropertyName.GetStringAt(0); if(wszPrimaryName == NULL || (_wcsicmp(wszPrimaryName, TARGET_INSTANCE_PROPNAME) && _wcsicmp(wszPrimaryName, PREVIOUS_INSTANCE_PROPNAME)) ) { // This token is irrelevant // ========================= return WBEM_S_NO_ERROR; } // TargetInstance or PreviousInstance is found // =========================================== if(Token.PropertyName.GetNumElements() == 1) { // It's "TargetInstance " : look for ISA // ================================================= if(Token.nOperator == QL1_OPERATOR_ISA && V_VT(&Token.vConstValue) == VT_BSTR) { // Of this class; children included // ================================ if(!aInfos.SetOne(V_BSTR(&Token.vConstValue), TRUE)) return WBEM_E_OUT_OF_MEMORY; } else { // No information // ============== } return WBEM_S_NO_ERROR; } if(Token.PropertyName.GetNumElements() > 2) { // X.Y.Z --- too deep to be useful // =============================== return WBEM_S_NO_ERROR; } // It's "TargetInstance.X " : look for __CLASS // ======================================================= LPCWSTR wszSecondaryName = Token.PropertyName.GetStringAt(1); if(wszSecondaryName == NULL || _wcsicmp(wszSecondaryName, L"__CLASS")) { // Not __CLASS --- not useful // ========================== return WBEM_S_NO_ERROR; } else { // __CLASS --- check that the operator is = // ======================================== if(Token.nOperator == QL1_OPERATOR_EQUALS && V_VT(&Token.vConstValue) == VT_BSTR) { // Of this class -- children not included // ====================================== if(!aInfos.SetOne(V_BSTR(&Token.vConstValue), FALSE)) return WBEM_E_OUT_OF_MEMORY; } return WBEM_S_NO_ERROR; } } HRESULT CQueryAnalyser::GetNecessaryQueryForProperty( IN QL_LEVEL_1_RPN_EXPRESSION* pExpr, IN CPropertyName& PropName, DELETE_ME QL_LEVEL_1_RPN_EXPRESSION*& pNewExpr) { pNewExpr = NULL; // Class name and selected properties are ignored; we look at tokens only // ====================================================================== std::stack > > ExprStack; HRESULT hres = WBEM_S_NO_ERROR; // "Evaluate" the query // ==================== if(pExpr->nNumTokens == 0) { // Empty query --- no information // ============================== pNewExpr = _new QL_LEVEL_1_RPN_EXPRESSION; if(pNewExpr == NULL) return WBEM_E_OUT_OF_MEMORY; return WBEM_S_NO_ERROR; } for(int i = 0; i < pExpr->nNumTokens; i++) { QL_LEVEL_1_TOKEN& Token = pExpr->pArrayOfTokens[i]; QL_LEVEL_1_RPN_EXPRESSION* pNew = _new QL_LEVEL_1_RPN_EXPRESSION; if(pNew == NULL) return WBEM_E_OUT_OF_MEMORY; QL_LEVEL_1_RPN_EXPRESSION* pFirst; QL_LEVEL_1_RPN_EXPRESSION* pSecond; switch(Token.nTokenType) { case QL1_OP_EXPRESSION: if(IsTokenAboutProperty(Token, PropName)) { pNew->AddToken(Token); } ExprStack.push(pNew); break; case QL1_AND: if(ExprStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); pSecond = ExprStack.top(); ExprStack.pop(); hres = AndQueryExpressions(pFirst, pSecond, pNew); ExprStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_OR: if(ExprStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); pSecond = ExprStack.top(); ExprStack.pop(); hres = OrQueryExpressions(pFirst, pSecond, pNew); ExprStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_NOT: if(ExprStack.size() < 1) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); // No information ExprStack.push(pNew); delete pFirst; break; default: hres = WBEM_E_CRITICAL_ERROR; delete pNew; } if(FAILED(hres)) { // An error occurred, break out of the loop // ======================================== break; } } if(SUCCEEDED(hres) && ExprStack.size() != 1) { hres = WBEM_E_CRITICAL_ERROR; } if(FAILED(hres)) { // An error occurred. Clear the stack // ================================== while(!ExprStack.empty()) { delete ExprStack.top(); ExprStack.pop(); } return hres; } // All is good // =========== pNewExpr = ExprStack.top(); return S_OK; } BOOL CQueryAnalyser::IsTokenAboutProperty( IN QL_LEVEL_1_TOKEN& Token, IN CPropertyName& PropName) { CPropertyName& TokenPropName = Token.PropertyName; if(PropName.GetNumElements() != TokenPropName.GetNumElements()) return FALSE; for(int i = 0; i < PropName.GetNumElements(); i++) { LPCWSTR wszPropElement = PropName.GetStringAt(i); LPCWSTR wszTokenElement = TokenPropName.GetStringAt(i); if(wszPropElement == NULL || wszTokenElement == NULL) return FALSE; if(_wcsicmp(wszPropElement, wszTokenElement)) return FALSE; } return TRUE; } void CQueryAnalyser::AppendQueryExpression( IN QL_LEVEL_1_RPN_EXPRESSION* pDest, IN QL_LEVEL_1_RPN_EXPRESSION* pSource) { for(int i = 0; i < pSource->nNumTokens; i++) { pDest->AddToken(pSource->pArrayOfTokens[i]); } } HRESULT CQueryAnalyser::AndQueryExpressions( IN QL_LEVEL_1_RPN_EXPRESSION* pFirst, IN QL_LEVEL_1_RPN_EXPRESSION* pSecond, OUT QL_LEVEL_1_RPN_EXPRESSION* pNew) { // If either one is NULL (false), the result is NULL // ================================================= if(pFirst == NULL || pSecond == NULL) return WBEM_S_FALSE; // If either one is empty, take the other // ====================================== if(pFirst->nNumTokens == 0) { AppendQueryExpression(pNew, pSecond); return WBEM_S_NO_ERROR; } if(pSecond->nNumTokens == 0) { AppendQueryExpression(pNew, pFirst); return WBEM_S_NO_ERROR; } // Both are there --- and together // =============================== AppendQueryExpression(pNew, pFirst); AppendQueryExpression(pNew, pSecond); QL_LEVEL_1_TOKEN Token; Token.nTokenType = QL1_AND; pNew->AddToken(Token); return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::OrQueryExpressions( IN QL_LEVEL_1_RPN_EXPRESSION* pFirst, IN QL_LEVEL_1_RPN_EXPRESSION* pSecond, OUT QL_LEVEL_1_RPN_EXPRESSION* pNew) { // If both are NULL (false) so is the result // ========================================= if(pFirst == NULL && pSecond == NULL) return WBEM_S_FALSE; // If one is NULL (false) return the other // ======================================= if(pFirst == NULL) { AppendQueryExpression(pNew, pSecond); return WBEM_S_NO_ERROR; } if(pSecond == NULL) { AppendQueryExpression(pNew, pFirst); return WBEM_S_NO_ERROR; } // If either one is empty, so is the result // ======================================== if(pFirst->nNumTokens == 0 || pSecond->nNumTokens == 0) { return WBEM_S_NO_ERROR; } // Both are there --- or together // ============================== AppendQueryExpression(pNew, pFirst); AppendQueryExpression(pNew, pSecond); QL_LEVEL_1_TOKEN Token; Token.nTokenType = QL1_OR; pNew->AddToken(Token); return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::GetPropertiesThatMustDiffer( IN QL_LEVEL_1_RPN_EXPRESSION* pExpr, IN CClassInformation& Info, CWStringArray& awsProperties) { HRESULT hres = WBEM_S_NO_ERROR; // // "Evaluate" the query, looking for // PreviousInstance.Prop != TargetInstance.Prop expressions // awsProperties.Empty(); std::stack > > PropArrayStack; for(int i = 0; i < pExpr->nNumTokens; i++) { QL_LEVEL_1_TOKEN& Token = pExpr->pArrayOfTokens[i]; CWStringArray* pNew = NULL; CWStringArray* pFirst = NULL; CWStringArray* pSecond = NULL; switch(Token.nTokenType) { case QL1_OP_EXPRESSION: // // Check if this token conforms to the // PreviousInstance.Prop != TargetInstance.Prop format // if(Token.m_bPropComp && (Token.nOperator == QL1_OPERATOR_NOTEQUALS || Token.nOperator == QL1_OPERATOR_LESS || Token.nOperator == QL1_OPERATOR_GREATER) && Token.PropertyName.GetNumElements() == 2 && Token.PropertyName2.GetNumElements() == 2) { // // Make sure that one of them is talking about TargetInstance, // and another about PreviousInstance. // bool bRightForm = false; if(!wbem_wcsicmp(Token.PropertyName.GetStringAt(0), L"TargetInstance") && !wbem_wcsicmp(Token.PropertyName2.GetStringAt(0), L"PreviousInstance")) { bRightForm = true; } if(!wbem_wcsicmp(Token.PropertyName.GetStringAt(0), L"PreviousInstance") && !wbem_wcsicmp(Token.PropertyName2.GetStringAt(0), L"TargetInstance")) { bRightForm = true; } if(bRightForm) { pNew = new CWStringArray; if(pNew == NULL) return WBEM_E_OUT_OF_MEMORY; pNew->Add(Token.PropertyName.GetStringAt(1)); } } PropArrayStack.push(pNew); break; case QL1_AND: if(PropArrayStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = PropArrayStack.top(); PropArrayStack.pop(); pSecond = PropArrayStack.top(); PropArrayStack.pop(); // // If either one of them is non-NULL, take either --- since every // array means "no unless one of these properties is different", // adding them together is at least as good as having one // if(pFirst) { pNew = pFirst; delete pSecond; } else pNew = pSecond; PropArrayStack.push(pNew); break; case QL1_OR: if(PropArrayStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = PropArrayStack.top(); PropArrayStack.pop(); pSecond = PropArrayStack.top(); PropArrayStack.pop(); // // Concatenate them --- since every // array means "no unless one of these properties is different", // oring them together means "no unless one of the properties in // either list is different". If one is NULL, though, then we know // nothing // if(pFirst && pSecond) { pNew = new CWStringArray; if(pNew == NULL) return WBEM_E_OUT_OF_MEMORY; CWStringArray::Union(*pFirst, *pSecond, *pNew); } PropArrayStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_NOT: if(PropArrayStack.size() < 1) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = PropArrayStack.top(); PropArrayStack.pop(); // No information PropArrayStack.push(pNew); delete pFirst; break; default: hres = WBEM_E_CRITICAL_ERROR; delete pNew; } if(FAILED(hres)) { // An error occurred, break out of the loop // ======================================== break; } } if( SUCCEEDED(hres)) { if( PropArrayStack.size() > 0 && PropArrayStack.top() ) awsProperties = *PropArrayStack.top(); else return WBEM_S_FALSE; } while(!PropArrayStack.empty()) { delete PropArrayStack.top(); PropArrayStack.pop(); } return hres; } HRESULT CQueryAnalyser::GetLimitingQueryForInstanceClass( IN QL_LEVEL_1_RPN_EXPRESSION* pExpr, IN CClassInformation& Info, OUT DELETE_ME LPWSTR& wszQuery) { HRESULT hres; // // "Evaluate" the query, looking for keys and other properties that do not // change over the life time of an instance (marked as [fixed]). The idea // here is that if an instance creation/deletion/modification subscription // is issue and we need to poll, we can only utilize the parts of the WHERE // clause that talk about the properties that cannot change during the life // of an instance. Otherwise, we will not be able to tell if an instance // changed or was created or deleted (when it walks in or out of our polling // results. // // The way we know that a property is such is if it is marked as [key], or // if it is marked as [fixed] --- the designation by the schema creator that // the property never changes. // // // Construct an array of all those property names // _IWmiObject* pClass = NULL; hres = Info.m_pClass->QueryInterface(IID__IWmiObject, (void**)&pClass); if(FAILED(hres)) return WBEM_E_CRITICAL_ERROR; CReleaseMe rm1(pClass); CWStringArray awsFixed; hres = pClass->BeginEnumeration(0); if(FAILED(hres)) return hres; BSTR strPropName = NULL; while((hres = pClass->Next(0, &strPropName, NULL, NULL, NULL)) == S_OK) { CSysFreeMe sfm(strPropName); // // Check qualifiers // DWORD dwSize; hres = pClass->GetPropQual(strPropName, L"key", 0, 0, NULL, NULL, &dwSize, NULL); if(SUCCEEDED(hres) || hres == WBEM_E_BUFFER_TOO_SMALL) { awsFixed.Add(strPropName); } else if(hres != WBEM_E_NOT_FOUND) { return hres; } hres = pClass->GetPropQual(strPropName, L"fixed", 0, 0, NULL, NULL, &dwSize, NULL); if(SUCCEEDED(hres) || hres == WBEM_E_BUFFER_TOO_SMALL) { awsFixed.Add(strPropName); } else if(hres != WBEM_E_NOT_FOUND) { return hres; } } pClass->EndEnumeration(); if(FAILED(hres)) return hres; // // Now "evaluate" the query // std::stack > > ExprStack; for(int i = 0; i < pExpr->nNumTokens; i++) { QL_LEVEL_1_TOKEN& Token = pExpr->pArrayOfTokens[i]; QL_LEVEL_1_RPN_EXPRESSION* pNew = _new QL_LEVEL_1_RPN_EXPRESSION; if(pNew == NULL) return WBEM_E_OUT_OF_MEMORY; QL_LEVEL_1_RPN_EXPRESSION* pFirst; QL_LEVEL_1_RPN_EXPRESSION* pSecond; switch(Token.nTokenType) { case QL1_OP_EXPRESSION: if(Token.PropertyName.GetNumElements() > 1 && awsFixed.FindStr(Token.PropertyName.GetStringAt(1), CWStringArray::no_case) != CWStringArray::not_found) { // // This token is about a fixed property --- we can keep it // QL_LEVEL_1_TOKEN NewToken = Token; NewToken.PropertyName.Empty(); NewToken.PropertyName.AddElement( Token.PropertyName.GetStringAt(1)); pNew->AddToken(NewToken); } ExprStack.push(pNew); break; case QL1_AND: if(ExprStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); pSecond = ExprStack.top(); ExprStack.pop(); hres = AndQueryExpressions(pFirst, pSecond, pNew); ExprStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_OR: if(ExprStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); pSecond = ExprStack.top(); ExprStack.pop(); hres = OrQueryExpressions(pFirst, pSecond, pNew); ExprStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_NOT: if(ExprStack.size() < 1) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); // No information ExprStack.push(pNew); delete pFirst; break; default: hres = WBEM_E_CRITICAL_ERROR; delete pNew; } if(FAILED(hres)) { // An error occurred, break out of the loop // ======================================== break; } } if(FAILED(hres)) { // // An error occurred. Clear the stack // while(!ExprStack.empty()) { delete ExprStack.top(); ExprStack.pop(); } return hres; } QL_LEVEL_1_RPN_EXPRESSION* pNewExpr = NULL; if(ExprStack.size() != 0) { pNewExpr = ExprStack.top(); } else { pNewExpr = new QL_LEVEL_1_RPN_EXPRESSION; if(pNewExpr == NULL) return WBEM_E_OUT_OF_MEMORY; } CDeleteMe dm1(pNewExpr); // // Figure out the list of property names // bool bMayLimit; if(pExpr->bStar) { bMayLimit = false; } else if(wbem_wcsicmp(pExpr->bsClassName, L"__InstanceCreationEvent") && wbem_wcsicmp(pExpr->bsClassName, L"__InstanceDeletionEvent")) { // // Instance modification events are included. That means we need // to get enough properties from the provider to be able to compare // instances for changes. Check if this list is smaller than // everything // CWStringArray awsProperties; hres = GetPropertiesThatMustDiffer(pExpr, Info, awsProperties); if(hres == S_OK) { // // Got our list --- add it to the properties to get // for(int i = 0; i < awsProperties.Size(); i++) { CPropertyName NewProp; NewProp.AddElement(awsProperties[i]); pNewExpr->AddProperty(NewProp); } bMayLimit = true; } else bMayLimit = false; } else { // // No * in select and no modification events asked for --- limit // bMayLimit = true; } if(bMayLimit) { // // Add RELPATH and DERIVATION, for without them filtering is hard // CPropertyName NewProp; NewProp.AddElement(L"__RELPATH"); pNewExpr->AddProperty(NewProp); NewProp.Empty(); NewProp.AddElement(L"__DERIVATION"); pNewExpr->AddProperty(NewProp); // // Add all the proeperties from the select clause, with // TargetInstance and PreviousInstance removed // for(int i = 0; i < pExpr->nNumberOfProperties; i++) { CPropertyName& Prop = pExpr->pRequestedPropertyNames[i]; if(Prop.GetNumElements() > 1) { // // Embedded object property --- add it to the list // CPropertyName LocalProp; LocalProp.AddElement(Prop.GetStringAt(1)); pNewExpr->AddProperty(LocalProp); } } // // Add all the properties from the where clause, on both sides of // the comparison // for(i = 0; i < pExpr->nNumTokens; i++) { QL_LEVEL_1_TOKEN& Token = pExpr->pArrayOfTokens[i]; CPropertyName& Prop = Token.PropertyName; if(Prop.GetNumElements() > 1) { // // Embedded object property --- add it to the list // CPropertyName LocalProp; LocalProp.AddElement(Prop.GetStringAt(1)); pNewExpr->AddProperty(LocalProp); } if(Token.m_bPropComp) { CPropertyName& Prop2 = Token.PropertyName2; if(Prop2.GetNumElements() > 1) { // // Embedded object property --- add it to the list // CPropertyName LocalProp; LocalProp.AddElement(Prop2.GetStringAt(1)); pNewExpr->AddProperty(LocalProp); } } } } else { // // May not limit the set of properties to ask for // pNewExpr->bStar = TRUE; } // // Set the class name // pNewExpr->SetClassName(Info.m_wszClassName); // // Produce the text // wszQuery = pNewExpr->GetText(); if(wszQuery == NULL) return WBEM_E_OUT_OF_MEMORY; return WBEM_S_NO_ERROR; } BOOL CQueryAnalyser::CompareRequestedToProvided( CClassInfoArray& aRequestedInstanceClasses, CClassInfoArray& aProvidedInstanceClasses) { if(!aRequestedInstanceClasses.IsLimited() || !aProvidedInstanceClasses.IsLimited()) { // Provided provides all or client wants all --- they intersect. // ============================================================= return TRUE; } for(int nReqIndex = 0; nReqIndex < aRequestedInstanceClasses.GetNumClasses(); nReqIndex++) { CClassInformation* pRequestedClass = aRequestedInstanceClasses.GetClass(nReqIndex); LPWSTR wszRequestedClass = pRequestedClass->m_wszClassName; for(int nProvIndex = 0; nProvIndex < aProvidedInstanceClasses.GetNumClasses(); nProvIndex++) { // Check if this provided class is derived from the requested one // ============================================================== CClassInformation* pProvClass = aProvidedInstanceClasses.GetClass(nProvIndex); if(pProvClass->m_pClass != NULL && (pProvClass->m_pClass->InheritsFrom(pRequestedClass->m_wszClassName) == S_OK || pRequestedClass->m_pClass->InheritsFrom(pProvClass->m_wszClassName) == S_OK) ) { return TRUE; } } } return FALSE; } HRESULT CQueryAnalyser::NegateQueryExpression( IN QL_LEVEL_1_RPN_EXPRESSION* pExpr, OUT QL_LEVEL_1_RPN_EXPRESSION* pNewExpr) { if(pExpr == NULL) { // pNewExpr is empty --- true return WBEM_S_NO_ERROR; } if(pExpr->nNumTokens == 0) { return WBEM_S_FALSE; } AppendQueryExpression(pNewExpr, pExpr); QL_LEVEL_1_TOKEN Token; Token.nTokenType = QL1_NOT; pNewExpr->AddToken(Token); return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::SimplifyQueryForChild( IN QL_LEVEL_1_RPN_EXPRESSION* pExpr, LPCWSTR wszClassName, IWbemClassObject* pClass, CContextMetaData* pMeta, DELETE_ME QL_LEVEL_1_RPN_EXPRESSION*& pNewExpr) { pNewExpr = NULL; std::stack > > ExprStack; HRESULT hres = WBEM_S_NO_ERROR; // "Evaluate" the query // ==================== if(pExpr->nNumTokens == 0) { // Empty query --- no information // ============================== pNewExpr = _new QL_LEVEL_1_RPN_EXPRESSION; if(pNewExpr == NULL) return WBEM_E_OUT_OF_MEMORY; return WBEM_S_NO_ERROR; } for(int i = 0; i < pExpr->nNumTokens; i++) { QL_LEVEL_1_TOKEN Token = pExpr->pArrayOfTokens[i]; QL_LEVEL_1_RPN_EXPRESSION* pNew = _new QL_LEVEL_1_RPN_EXPRESSION; if(pNew == NULL) return WBEM_E_OUT_OF_MEMORY; QL_LEVEL_1_RPN_EXPRESSION* pFirst; QL_LEVEL_1_RPN_EXPRESSION* pSecond; int nDisposition; switch(Token.nTokenType) { case QL1_OP_EXPRESSION: nDisposition = SimplifyTokenForChild(Token, wszClassName, pClass, pMeta); if(nDisposition == e_Keep) { pNew->AddToken(Token); } else if(nDisposition == e_True) { } else if(nDisposition == e_False) { delete pNew; pNew = NULL; } else { // the whole thing is invalid hres = WBEM_E_INVALID_QUERY; delete pNew; break; } ExprStack.push(pNew); break; case QL1_AND: if(ExprStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); pSecond = ExprStack.top(); ExprStack.pop(); hres = AndQueryExpressions(pFirst, pSecond, pNew); if(hres != S_OK) { delete pNew; pNew = NULL; } ExprStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_OR: if(ExprStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); pSecond = ExprStack.top(); ExprStack.pop(); hres = OrQueryExpressions(pFirst, pSecond, pNew); if(hres != S_OK) { delete pNew; pNew = NULL; } ExprStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_NOT: if(ExprStack.size() < 1) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); hres = NegateQueryExpression(pFirst, pNew); if(hres != S_OK) { delete pNew; pNew = NULL; } ExprStack.push(pNew); delete pFirst; break; default: hres = WBEM_E_CRITICAL_ERROR; delete pNew; } if(FAILED(hres)) { // An error occurred, break out of the loop // ======================================== break; } } if(SUCCEEDED(hres) && ExprStack.size() != 1) { hres = WBEM_E_CRITICAL_ERROR; } if(FAILED(hres)) { // An error occurred. Clear the stack // ================================== while(!ExprStack.empty()) { delete ExprStack.top(); ExprStack.pop(); } return hres; } // All is good // =========== pNewExpr = ExprStack.top(); return S_OK; } int CQueryAnalyser::SimplifyTokenForChild(QL_LEVEL_1_TOKEN& Token, LPCWSTR wszClassName, IWbemClassObject* pClass, CContextMetaData* pMeta) { HRESULT hres; // // Check if the main property exists // CIMTYPE ct; hres = pClass->Get((LPWSTR)Token.PropertyName.GetStringAt(0), 0, NULL, &ct, NULL); if(FAILED(hres)) { return e_Invalid; } // // Check if it is complex // if(Token.PropertyName.GetNumElements() > 1 && ct != CIM_OBJECT) return e_Invalid; // // Check if it's an array // if(ct & CIM_FLAG_ARRAY) return e_Invalid; // // If a CIM DateTime type, normalize it to have a zero UTC offset. Helps // providers to cope. // if (ct == CIM_DATETIME && Token.m_bPropComp == FALSE && V_VT(&Token.vConstValue) == VT_BSTR) { BSTR strSource = V_BSTR(&Token.vConstValue); if (strSource && wcslen(strSource)) { BSTR strAdjusted = 0; BOOL bRes = NormalizeCimDateTime(strSource, &strAdjusted); if (bRes) { SysFreeString(strSource); V_BSTR(&Token.vConstValue) = strAdjusted; } } } // // Check operator validity for this type // // // Ensure that only valid operators are applied to boolean props. // if(ct == CIM_BOOLEAN && (Token.nOperator != QL_LEVEL_1_TOKEN::OP_EQUAL && Token.nOperator != QL_LEVEL_1_TOKEN::OP_NOT_EQUAL)) return e_Invalid; // // Ensure that only valid operators are applied to reference props. // if(ct == CIM_REFERENCE && (Token.nOperator != QL_LEVEL_1_TOKEN::OP_EQUAL && Token.nOperator != QL_LEVEL_1_TOKEN::OP_NOT_EQUAL)) return e_Invalid; if(Token.m_bPropComp) { // // Check if the other property exists // CIMTYPE ct2; hres = pClass->Get((LPWSTR)Token.PropertyName2.GetStringAt(0), 0, NULL, &ct2, NULL); if(FAILED(hres)) { return e_Invalid; } // // Check if it is complex // if(Token.PropertyName2.GetNumElements() > 1 && ct2 != CIM_OBJECT) return e_Invalid; // // Check if it's an array // if(ct2 & CIM_FLAG_ARRAY) return e_Invalid; // // Nothing else to say about prop-to-ptop // return e_Keep; } // // Check if the value is NULL // if(V_VT(&Token.vConstValue) == VT_NULL) { if(Token.nOperator != QL1_OPERATOR_EQUALS && Token.nOperator != QL1_OPERATOR_NOTEQUALS) { return e_Invalid; } else { return e_Keep; } } if(ct == CIM_OBJECT) return e_Keep; // For boolean props ensure that only 1 or 0 or (-1, 0xFFFF [VARIANT_TRUE]) // are used as numeric tests. // ======================================================================== if (ct == CIM_BOOLEAN && V_VT(&Token.vConstValue) == VT_I4) { int n = V_I4(&Token.vConstValue); if (n != 0 && n != 1 && n != -1 && n != 0xFFFF) return e_Invalid; } // // If the constant is a real and the target is an integer, then fail the // query // if((V_VT(&Token.vConstValue) == VT_R8 || V_VT(&Token.vConstValue) == VT_R4 ) && (ct == CIM_CHAR16 || ct == CIM_UINT8 || ct == CIM_SINT8 || ct == CIM_UINT16 || ct == CIM_SINT16 || ct == CIM_UINT32 || ct == CIM_SINT32 || ct == CIM_UINT64 || ct == CIM_SINT64)) return e_Invalid; // Convert the constant to the right type // ====================================== if(ct == CIM_CHAR16 && V_VT(&Token.vConstValue) == VT_BSTR) { BSTR str = V_BSTR(&Token.vConstValue); if(wcslen(str) != 1) return e_Invalid; return e_Keep; } VARTYPE vt = CType::GetVARTYPE(ct); if(ct == CIM_UINT32) vt = CIM_STRING; if(FAILED(VariantChangeType(&Token.vConstValue, &Token.vConstValue, 0, vt))) { return e_Invalid; } // Verify ranges // ============= __int64 i64; unsigned __int64 ui64; switch(ct) { case CIM_UINT8: break; case CIM_SINT8: if(V_I2(&Token.vConstValue) < -128 || V_I2(&Token.vConstValue) > 127) return e_Invalid; break; case CIM_UINT16: if(V_I4(&Token.vConstValue) < 0 || V_I4(&Token.vConstValue) >= 1<<16) return e_Invalid; break; case CIM_SINT16: break; case CIM_SINT32: break; case CIM_UINT32: if(!ReadI64(V_BSTR(&Token.vConstValue), i64)) return e_Invalid; if(i64 < 0 || i64 >= (__int64)1 << 32) return e_Invalid; break; case CIM_UINT64: if(!ReadUI64(V_BSTR(&Token.vConstValue), ui64)) return e_Invalid; break; case CIM_SINT64: if(!ReadI64(V_BSTR(&Token.vConstValue), i64)) return e_Invalid; break; case CIM_REAL32: case CIM_REAL64: break; case CIM_STRING: break; case CIM_DATETIME: if(!ValidateSQLDateTime(V_BSTR(&Token.vConstValue))) return e_Invalid; case CIM_REFERENCE: break; } // Check if it is a reference // ========================== if(ct != CIM_REFERENCE) return e_Keep; // Reference. Parse the path in the value // ====================================== if(V_VT(&Token.vConstValue) != VT_BSTR) return e_Keep; CObjectPathParser Parser; ParsedObjectPath* pOutput = NULL; int nRes = Parser.Parse(V_BSTR(&Token.vConstValue), &pOutput); if(nRes != CObjectPathParser::NoError) return e_Invalid; WString wsPathClassName = pOutput->m_pClass; BOOL bInstance = (pOutput->m_bSingletonObj || pOutput->m_dwNumKeys != 0); // TBD: analyse the path for validity delete pOutput; hres = CanPointToClass(pClass, (LPWSTR)Token.PropertyName.GetStringAt(0), wsPathClassName, pMeta); if(FAILED(hres)) return e_Invalid; else if(hres == WBEM_S_NO_ERROR) return e_Keep; else { // Equality can never be achieved. The token is either always true, // or always false, depending on the operator if(Token.nOperator == QL1_OPERATOR_EQUALS) return e_False; else return e_True; } } BOOL CQueryAnalyser::ValidateSQLDateTime(LPCWSTR wszDateTime) { #ifndef UNICODE char* szBuffer = new char[wcslen(wszDateTime)*4+1]; if(szBuffer == NULL) return FALSE; sprintf(szBuffer, "%S", wszDateTime); CDateTimeParser dtParser(szBuffer); delete [] szBuffer; #else CDateTimeParser dtParser(wszDateTime); #endif if(!dtParser.IsValidDateTime()) return FALSE; WCHAR wszDMTF[26]; dtParser.FillDMTF(wszDMTF); CWbemTime wt; if(!wt.SetDMTF(wszDMTF)) return FALSE; return TRUE; } HRESULT CQueryAnalyser::CanPointToClass(IWbemClassObject* pRefClass, LPCWSTR wszPropName, LPCWSTR wszTargetClassName, CContextMetaData* pMeta) { // Check if the reference is typed // =============================== IWbemQualifierSet* pSet; if(FAILED(pRefClass->GetPropertyQualifierSet((LPWSTR)wszPropName, &pSet))) { return WBEM_E_INVALID_PROPERTY; } VARIANT v; HRESULT hres; hres = pSet->Get(L"cimtype", 0, &v, NULL); pSet->Release(); if(FAILED(hres) || V_VT(&v) != VT_BSTR) return WBEM_E_INVALID_PROPERTY; CClearMe cm(&v); if(wbem_wcsicmp(V_BSTR(&v), L"ref") == 0) return WBEM_S_NO_ERROR; // can point to anything WString wsPropClassName = V_BSTR(&v) + 4; // Reference is strongly typed. // ============================ if(!_wcsicmp(wsPropClassName, wszTargetClassName)) return WBEM_S_NO_ERROR; // Retrieve class def // ================== _IWmiObject* pPropClass = NULL; hres = pMeta->GetClass(wsPropClassName, &pPropClass); if(FAILED(hres)) return hres; CReleaseMe rm1((IWbemClassObject*)pPropClass); // Make sure that the class in the reference is related to our cimtype // =================================================================== if(pPropClass->InheritsFrom((LPWSTR)wszTargetClassName) != S_OK) { // Get the class in the path to see if it inherits from us // ======================================================= _IWmiObject* pPathClass = NULL; hres = pMeta->GetClass(wszTargetClassName, &pPathClass); if(FAILED(hres)) return hres; hres = pPathClass->InheritsFrom(wsPropClassName); pPathClass->Release(); if(hres != S_OK) { return WBEM_S_FALSE; } } return WBEM_S_NO_ERROR; } HRESULT CQueryAnalyser::GetNecessaryQueryForClass( IN QL_LEVEL_1_RPN_EXPRESSION* pExpr, IWbemClassObject* pClass, CWStringArray& awsOverriden, DELETE_ME QL_LEVEL_1_RPN_EXPRESSION*& pNewExpr) { pNewExpr = NULL; // Class name and selected properties are ignored; we look at tokens only // ====================================================================== std::stack > > ExprStack; HRESULT hres = WBEM_S_NO_ERROR; // "Evaluate" the query // ==================== for(int i = 0; i < pExpr->nNumTokens; i++) { QL_LEVEL_1_TOKEN& Token = pExpr->pArrayOfTokens[i]; QL_LEVEL_1_RPN_EXPRESSION* pNew = _new QL_LEVEL_1_RPN_EXPRESSION; if(pNew == NULL) return WBEM_E_OUT_OF_MEMORY; QL_LEVEL_1_RPN_EXPRESSION* pFirst; QL_LEVEL_1_RPN_EXPRESSION* pSecond; switch(Token.nTokenType) { case QL1_OP_EXPRESSION: if(IsTokenAboutClass(Token, pClass, awsOverriden)) { pNew->AddToken(Token); } ExprStack.push(pNew); break; case QL1_AND: if(ExprStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); pSecond = ExprStack.top(); ExprStack.pop(); hres = AndQueryExpressions(pFirst, pSecond, pNew); ExprStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_OR: if(ExprStack.size() < 2) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); pSecond = ExprStack.top(); ExprStack.pop(); hres = OrQueryExpressions(pFirst, pSecond, pNew); ExprStack.push(pNew); delete pFirst; delete pSecond; break; case QL1_NOT: if(ExprStack.size() < 1) { hres = WBEM_E_CRITICAL_ERROR; break; } pFirst = ExprStack.top(); ExprStack.pop(); // No information ExprStack.push(pNew); delete pFirst; break; default: hres = WBEM_E_CRITICAL_ERROR; delete pNew; } if(FAILED(hres)) { // An error occurred, break out of the loop // ======================================== break; } } if(FAILED(hres)) { // An error occurred. Clear the stack // ================================== while(!ExprStack.empty()) { delete ExprStack.top(); ExprStack.pop(); } return hres; } if(pExpr->nNumTokens == 0) { // Empty query --- stays empty pNewExpr = _new QL_LEVEL_1_RPN_EXPRESSION; if(pNewExpr == NULL) return WBEM_E_OUT_OF_MEMORY; } else if(ExprStack.size() != 1) { // internal error return WBEM_E_CRITICAL_ERROR; } else { // All is good // =========== pNewExpr = ExprStack.top(); } // // Copy the class name // VARIANT vName; hres = pClass->Get(L"__CLASS", 0, &vName, NULL, NULL); if(FAILED(hres)) return WBEM_E_CRITICAL_ERROR; pNewExpr->bsClassName = V_BSTR(&vName); // Variant intentionally not cleared // // Copy all the properties in the select clause except for irrelevant ones // pNewExpr->bStar = pExpr->bStar; if(!pNewExpr->bStar) { delete [] pNewExpr->pRequestedPropertyNames; pNewExpr->nCurPropSize = pExpr->nCurPropSize+1; pNewExpr->pRequestedPropertyNames = new CPropertyName[pNewExpr->nCurPropSize]; if(pNewExpr->pRequestedPropertyNames == NULL) { delete pNewExpr; return WBEM_E_OUT_OF_MEMORY; } // // Add __RELPATH, as we always need that! // pNewExpr->pRequestedPropertyNames[0].AddElement(L"__RELPATH"); pNewExpr->nNumberOfProperties = 1; for(int i = 0; i < pExpr->nNumberOfProperties; i++) { // // Check if the property exists in the class // CIMTYPE ct; hres = pClass->Get(pExpr->pRequestedPropertyNames[i].GetStringAt(0), 0, NULL, &ct, NULL); if(SUCCEEDED(hres)) { // // Add it to the list // pNewExpr->pRequestedPropertyNames[ pNewExpr->nNumberOfProperties++] = pExpr->pRequestedPropertyNames[i]; } } } return S_OK; } BOOL CQueryAnalyser::IsTokenAboutClass(QL_LEVEL_1_TOKEN& Token, IWbemClassObject* pClass, CWStringArray& awsOverriden) { // // Check if the property being compared is in our class // and not overriden // if(!IsPropertyInClass(Token.PropertyName, pClass, awsOverriden)) return FALSE; // // If comparing to another property, check if that one is // likewise good // if(Token.m_bPropComp && !IsPropertyInClass(Token.PropertyName2, pClass, awsOverriden)) return FALSE; return TRUE; } BOOL CQueryAnalyser::IsPropertyInClass(CPropertyName& Prop, IWbemClassObject* pClass, CWStringArray& awsOverriden) { // // Check if the property exists in the class // CIMTYPE ct; HRESULT hres = pClass->Get(Prop.GetStringAt(0), 0, NULL, &ct, NULL); if(FAILED(hres)) return FALSE; // // Check if the property is overriden by any of our children // if(awsOverriden.FindStr(Prop.GetStringAt(0), CWStringArray::no_case) >= 0) return FALSE; return TRUE; }