//*************************************************************************** // // Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved // // FRQuery.cpp // // Purpose: Query functions // //*************************************************************************** #include "precomp.h" #include #include #include #include #include #include #include #include #include "multiplat.h" CFrameworkQuery::CFrameworkQuery() { m_pLevel1RPNExpression = NULL; m_QueryType = eUnknown; m_bKeysOnly = false; m_IClass = NULL; m_lFlags = 0; } CFrameworkQuery::~CFrameworkQuery() { if (m_pLevel1RPNExpression) { delete m_pLevel1RPNExpression; } if (m_IClass) { m_IClass->Release(); } } HRESULT CFrameworkQuery::Init( const BSTR bstrQueryFormat, const BSTR bstrQuery, long lFlags, CHString &sNamespace ) { HRESULT hRes = WBEM_S_NO_ERROR; // Clear out any old values Reset(); // Start setting our values m_lFlags = lFlags; m_bstrtClassName = L""; m_QueryType = eWQLCommand; m_sNamespace = sNamespace; // Check for the obvious if (_wcsicmp(bstrQueryFormat, IDS_WQL) != 0) { hRes = WBEM_E_INVALID_QUERY_TYPE; LogErrorMessage2(L"Invalid query type: %s", bstrQueryFormat); } if (hRes == WBEM_S_NO_ERROR) { // Construct the lex source // ======================== CTextLexSource LexSource(bstrQuery); // Use the lex source to set up for parser // ======================================= SQL1_Parser QueryParser(&LexSource); int ParseRetValue = QueryParser.Parse(&m_pLevel1RPNExpression); if( SQL1_Parser::SUCCESS == ParseRetValue) { // Store some common values m_bstrtClassName = m_pLevel1RPNExpression->bsClassName; m_sQuery = bstrQuery; // Build the Requested Properies Array (m_csaPropertiesRequired) if (m_pLevel1RPNExpression->nNumberOfProperties > 0) { // Populate the m_csaPropertiesRequired array with all the required properties CHString sPropertyName; // First add the elements of the Select clause for (DWORD x=0; x < m_pLevel1RPNExpression->nNumberOfProperties; x++) { sPropertyName = m_pLevel1RPNExpression->pbsRequestedPropertyNames[x]; sPropertyName.MakeUpper(); if (IsInList(m_csaPropertiesRequired, sPropertyName) == -1) { m_csaPropertiesRequired.Add(sPropertyName); } } // Then add the elements of the where clause for (x=0; x < m_pLevel1RPNExpression->nNumTokens; x++) { if (m_pLevel1RPNExpression->pArrayOfTokens[x].nTokenType == SQL_LEVEL_1_TOKEN::OP_EXPRESSION) { sPropertyName = m_pLevel1RPNExpression->pArrayOfTokens[x].pPropertyName; sPropertyName.MakeUpper(); if (IsInList(m_csaPropertiesRequired, sPropertyName) == -1) { m_csaPropertiesRequired.Add(sPropertyName); } if (m_pLevel1RPNExpression->pArrayOfTokens[x].pPropName2 != NULL) { sPropertyName = m_pLevel1RPNExpression->pArrayOfTokens[x].pPropName2; sPropertyName.MakeUpper(); if (IsInList(m_csaPropertiesRequired, sPropertyName) == -1) { m_csaPropertiesRequired.Add(sPropertyName); } } } } } } else { ASSERT_BREAK(FALSE); m_pLevel1RPNExpression = NULL; LogErrorMessage2(L"Can't parse query: %s", bstrQuery); hRes = WBEM_E_INVALID_QUERY; } } return hRes; } HRESULT CFrameworkQuery::Init( ParsedObjectPath *pParsedObjectPath, IWbemContext *pCtx, LPCWSTR lpwszClassName, CHString &sNamespace ) { HRESULT hr = WBEM_S_NO_ERROR; variant_t vValue; // Clear out any old values Reset(); // Start setting our values m_bstrtClassName = lpwszClassName; m_QueryType = eContextObject; m_lFlags = 0; m_sNamespace = sNamespace; // Check to see if get extensions are being used if ( (pCtx != NULL) && (SUCCEEDED(pCtx->GetValue( L"__GET_EXTENSIONS", 0, &vValue))) && (V_VT(&vValue) == VT_BOOL) && (V_BOOL(&vValue) == VARIANT_TRUE) ) { vValue.Clear(); bool bKeysRequired = false; // Ok, did they ask for KeysOnly? // __GET_EXT_PROPERTIES and __GET_EXT_KEYS_ONLY are mutually exclusive. If they // specified KeysOnly, we'll go with that. if ( (SUCCEEDED(pCtx->GetValue( L"__GET_EXT_KEYS_ONLY", 0, &vValue))) && (V_VT(&vValue) == VT_BOOL) && (V_BOOL(&vValue) == VARIANT_TRUE) ) { LogMessage(L"Recognized __GET_EXT_KEYS_ONLY"); m_bKeysOnly = true; bKeysRequired = true; } else { vValue.Clear(); if ( (SUCCEEDED(pCtx->GetValue( L"__GET_EXT_PROPERTIES", 0, &vValue))) && (V_VT(&vValue) == (VT_ARRAY | VT_BSTR) ) && ( SafeArrayGetDim ( V_ARRAY(&vValue) ) == 1 ) ) { LogMessage(L"Recognized __GET_EXT_PROPERTIES"); // Ok, they sent us an arry of properties. Add them to m_csaPropertiesRequired. LONG lDimension = 1 ; LONG lLowerBound ; SafeArrayGetLBound ( V_ARRAY(&vValue) , lDimension , & lLowerBound ) ; LONG lUpperBound ; SafeArrayGetUBound ( V_ARRAY(&vValue) , lDimension , & lUpperBound ) ; CHString sPropertyName; for ( long lIndex = lLowerBound ; lIndex <= lUpperBound ; lIndex ++ ) { BSTR bstrElement ; HRESULT t_Result = SafeArrayGetElement ( V_ARRAY(&vValue), &lIndex , & bstrElement ) ; if ( (t_Result == S_OK) && (bstrElement != NULL) ) { try { sPropertyName = bstrElement; } catch ( ... ) { SysFreeString(bstrElement); throw; } SysFreeString(bstrElement); sPropertyName.MakeUpper(); if (IsInList(m_csaPropertiesRequired, sPropertyName) == -1) { m_csaPropertiesRequired.Add(sPropertyName); } } } if ( (IsInList(m_csaPropertiesRequired, L"__RELPATH") != -1) || (IsInList(m_csaPropertiesRequired, L"__PATH") != -1) ) { bKeysRequired = true; } } } // If they specified KeysOnly or __RELPATH or __Path, we need to add the key properties // to the list. if (bKeysRequired) { if ((pParsedObjectPath != NULL) && (pParsedObjectPath->m_dwNumKeys > 0) && (pParsedObjectPath->m_paKeys[0]->m_pName != NULL)) { CHString sPropertyName; for (DWORD x=0; x < pParsedObjectPath->m_dwNumKeys; x++) { sPropertyName = pParsedObjectPath->m_paKeys[x]->m_pName; sPropertyName.MakeUpper(); if (IsInList(m_csaPropertiesRequired, sPropertyName) == -1) { m_csaPropertiesRequired.Add(sPropertyName); } } m_AddKeys = false; } else if ( (pParsedObjectPath != NULL) && (pParsedObjectPath->m_bSingletonObj) ) { m_AddKeys = false; } else { // If they didn't give us a pParsedObjectPath or if the object path doesn't contain // the key property name, best we can do is add relpath. Hopefully they'll call // init2, and it will add the rest. if (IsInList(m_csaPropertiesRequired, L"__RELPATH") == -1) { m_csaPropertiesRequired.Add(L"__RELPATH"); } } } } return hr; } // =================================================================================================== // Finds out if a particular field was requested by the query. Only // meaningful if we are in ExecQueryAsync and the query has been // sucessfully parsed. bool CFrameworkQuery::IsPropertyRequired( LPCWSTR propName ) { bool bRet = AllPropertiesAreRequired(); if (!bRet) { CHString sPropName(propName); sPropName.MakeUpper(); bRet = (IsInList(m_csaPropertiesRequired, sPropName) != -1); } return bRet; } // Given a property name, it will return all the values // that the query requests in a CHStringArray. // Select * from win32_directory where drive = "C:" GetValuesForProp(L"Drive") -> C: // Where Drive = "C:" or Drive = "D:" GetValuesForProp(L"Drive") -> C:, D: // Where Path = "\DOS" GetValuesForProp(L"Drive") -> (empty) // Where Drive <> "C:" GetValuesForProp(L"Drive") -> (empty) HRESULT CFrameworkQuery::GetValuesForProp( LPCWSTR wszPropName, CHStringArray& achNames ) { HRESULT hr = WBEM_S_NO_ERROR; if (wszPropName && (m_pLevel1RPNExpression != NULL)) { hr = CQueryAnalyser::GetValuesForProp(m_pLevel1RPNExpression, wszPropName, achNames); if (SUCCEEDED(hr)) { // If this is a reference property, we need to normalize the names to a common form // so the removal of duplicates works correctly. if (IsReference(wszPropName)) { // Get the current computer name CHString sOutPath, sComputerName; DWORD dwBufferLength = MAX_COMPUTERNAME_LENGTH + 1; FRGetComputerName(sComputerName.GetBuffer( dwBufferLength ), &dwBufferLength); sComputerName.ReleaseBuffer(); if (sComputerName.IsEmpty()) { sComputerName = L"DEFAULT"; } DWORD dwRet = e_OK; // Normalize the path names. Try leaving the property names alone for (int x = 0; x < achNames.GetSize(); x++) { // If we failed to parse the path, or if the namespace isn't our namespace, delete // the entry. dwRet = NormalizePath(achNames[x], sComputerName, GetNamespace(), 0, sOutPath); if (dwRet == e_OK) { achNames[x] = sOutPath; } else if (dwRet == e_NullName) { break; } else { achNames.RemoveAt(x); x--; } } // If the key property names of any of the values were null, we have to set them all // to null. if (dwRet == e_NullName) { // Normalize the path names for (int x = 0; x < achNames.GetSize(); x++) { // If we failed to parse the path, or if the namespace isn't our namespace, delete // the entry. dwRet = NormalizePath(achNames[x], sComputerName, GetNamespace(), NORMALIZE_NULL, sOutPath); if (dwRet == e_OK) { achNames[x] = sOutPath; } else { achNames.RemoveAt(x); x--; } } } } // Remove duplicates for (int x = 1; x < achNames.GetSize(); x++) { for (int y = 0; y < x; y++) { if (achNames[y].CompareNoCase(achNames[x]) == 0) { achNames.RemoveAt(x); x--; } } } } else { achNames.RemoveAll(); if (hr == WBEMESS_E_REGISTRATION_TOO_BROAD) { hr = WBEM_S_NO_ERROR; } } } else { ASSERT_BREAK(FALSE); achNames.RemoveAll(); hr = WBEM_E_FAILED; } return hr; } // Here's an overloaded version in case client wants to pass in a vector of _bstr_t's HRESULT CFrameworkQuery::GetValuesForProp( LPCWSTR wszPropName, std::vector<_bstr_t>& vectorNames ) { HRESULT hr = WBEM_S_NO_ERROR; if (wszPropName && (m_pLevel1RPNExpression != NULL) ) { hr = CQueryAnalyser::GetValuesForProp(m_pLevel1RPNExpression, wszPropName, vectorNames); if (SUCCEEDED(hr)) { // If this is a reference property, we need to normalize the names to a common form // so the removal of duplicates works correctly. if (IsReference(wszPropName)) { // Get the current computer name CHString sOutPath, sComputerName; DWORD dwBufferLength = MAX_COMPUTERNAME_LENGTH + 1; FRGetComputerName(sComputerName.GetBuffer( dwBufferLength ), &dwBufferLength); sComputerName.ReleaseBuffer(); if (sComputerName.IsEmpty()) { sComputerName = L"DEFAULT"; } DWORD dwRet = e_OK; // Normalize the path names. Try leaving the property names alone for (int x = 0; x < vectorNames.size(); x++) { // If we failed to parse the path, or if the namespace isn't our namespace, delete // the entry. dwRet = NormalizePath(vectorNames[x], sComputerName, GetNamespace(), 0, sOutPath); if (dwRet == e_OK) { vectorNames[x] = sOutPath; } else if (dwRet == e_NullName) { break; } else { vectorNames.erase(vectorNames.begin() + x); x--; } } // If the key property names of any of the values were null, we have to set them all // to null. if (dwRet == e_NullName) { for (int x = 0; x < vectorNames.size(); x++) { // If we failed to parse the path, or if the namespace isn't our namespace, delete // the entry. dwRet = NormalizePath(vectorNames[x], sComputerName, GetNamespace(), NORMALIZE_NULL, sOutPath); if (dwRet == e_OK) { vectorNames[x] = sOutPath; } else { vectorNames.erase(vectorNames.begin() + x); x--; } } } } // Remove duplicates for (int x = 1; x < vectorNames.size(); x++) { for (int y = 0; y < x; y++) { if (_wcsicmp(vectorNames[y], vectorNames[x]) == 0) { vectorNames.erase(vectorNames.begin() + x); x--; } } } } else { vectorNames.clear(); if (hr == WBEMESS_E_REGISTRATION_TOO_BROAD) { hr = WBEM_S_NO_ERROR; } } } else { ASSERT_BREAK(FALSE); vectorNames.clear(); hr = WBEM_E_FAILED; } return hr; } // Returns a list of all the properties specified in the select statement. // If * is specified as one of the fields, it is returned in the same way as all // other properties. void CFrameworkQuery::GetRequiredProperties( CHStringArray &saProperties ) { saProperties.RemoveAll(); saProperties.Copy(m_csaPropertiesRequired); } // Initializes the KeysOnly data member. Should never be called by users. void CFrameworkQuery::Init2( IWbemClassObject *IClass ) { // Store IClass object for use in GetValuesForProp m_IClass = IClass; m_IClass->AddRef(); // If KeysOnly get set somewhere else, or if we already know all properties are requried // there's no point in looking for non-key properties. if (!m_bKeysOnly && !AllPropertiesAreRequired()) { // First, we are going to correctly set the m_bKeysOnly member IWbemQualifierSetPtr pQualSet; HRESULT hr; DWORD dwSize = m_csaPropertiesRequired.GetSize(); m_bKeysOnly = true; for (DWORD x=0; x < dwSize; x++) { if (m_csaPropertiesRequired[x].Left(2) != L"__") { // If we fail here, it could be due to an invalid property name specified in the query. if (SUCCEEDED(hr = IClass->GetPropertyQualifierSet( m_csaPropertiesRequired[x] , &pQualSet))) { hr = pQualSet->Get( L"Key", 0, NULL, NULL); if (hr == WBEM_E_NOT_FOUND) { m_bKeysOnly = false; break; } else if (FAILED(hr)) { LogErrorMessage3(L"Can't Get 'key' on %s(%x)", (LPCWSTR)m_csaPropertiesRequired[x], hr); ASSERT_BREAK(FALSE); } } else { if (hr == WBEM_E_NOT_FOUND) { // This just means there are properties in the per-property list that don't exist hr = WBEM_S_NO_ERROR; } else { LogErrorMessage3(L"Can't get property GetPropertyQualifierSet on %s(%x)", (LPCWSTR)m_csaPropertiesRequired[x], hr); ASSERT_BREAK(FALSE); } } } } } // Second, if they specified a property list, and one of the properties was __path or __relpath, // then we need to add the name of the actual key properties to the list. Unless we added them // somewhere else. if ( m_AddKeys && !AllPropertiesAreRequired() && ( (IsInList(m_csaPropertiesRequired, L"__RELPATH") != -1) || (IsInList(m_csaPropertiesRequired, L"__PATH") != -1) ) ) { SAFEARRAY *pKeyNames = NULL; HRESULT hr; // Get the keys for the class if (SUCCEEDED(hr = IClass->GetNames(NULL, WBEM_FLAG_KEYS_ONLY, NULL, &pKeyNames))) { try { BSTR bstrName = NULL ; CHString sKeyName; LONG lLBound, lUBound; SafeArrayGetLBound(pKeyNames, 1, &lLBound); SafeArrayGetUBound(pKeyNames, 1, &lUBound); // Walk the key properties, and add any properties that // are not already in the list for (long i = lLBound; i <= lUBound; i++) { if (SUCCEEDED(SafeArrayGetElement( pKeyNames, &i, &bstrName ))) { try { sKeyName = bstrName; } catch ( ... ) { SysFreeString(bstrName); throw; } SysFreeString(bstrName); sKeyName.MakeUpper(); if (IsInList(m_csaPropertiesRequired, sKeyName) == -1) { m_csaPropertiesRequired.Add(sKeyName); } } else { throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ; } } } catch ( ... ) { SafeArrayDestroy(pKeyNames); throw; } SafeArrayDestroy(pKeyNames); } else { LogErrorMessage2(L"Failed to Get keys", hr); } } } const CHString &CFrameworkQuery::GetQuery() { if (m_QueryType == eContextObject) { if (m_sQuery.IsEmpty()) { if (AllPropertiesAreRequired()) { bstr_t t_Str ( GetQueryClassName() , FALSE) ; m_sQuery.Format(L"SELECT * FROM %s", (LPCWSTR)t_Str ); } else if (KeysOnly()) { bstr_t t_Str ( GetQueryClassName() , FALSE) ; m_sQuery.Format(L"SELECT __RELPATH FROM %s", (LPCWSTR)t_Str ); } else { m_sQuery = L"SELECT " + m_csaPropertiesRequired[0]; for (DWORD x=1; x < m_csaPropertiesRequired.GetSize(); x++) { m_sQuery += L", "; m_sQuery += m_csaPropertiesRequired[x]; } m_sQuery += L" FROM "; bstr_t t_Str ( GetQueryClassName() , FALSE) ; m_sQuery += t_Str ; } } } return m_sQuery; } /***************************************************************************** * * FUNCTION : IsInList * * DESCRIPTION : Checks to see if a specified element is in the list * * INPUTS : Array to scan, and element * * OUTPUTS : * * RETURNS : -1 if not in list, else zero based element number * * COMMENTS : This routine does a CASE SENSITIVE compare * *****************************************************************************/ DWORD CFrameworkQuery::IsInList( const CHStringArray &csaArray, LPCWSTR pwszValue ) { DWORD dwSize = csaArray.GetSize(); for (DWORD x=0; x < dwSize; x++) { // Note this is a CASE SENSITIVE compare if (wcscmp(csaArray[x], pwszValue) == 0) { return x; } } return -1; } /***************************************************************************** * * FUNCTION : Reset * * DESCRIPTION : Zeros out class data members * * INPUTS : * * OUTPUTS : * * RETURNS : * * COMMENTS : * *****************************************************************************/ void CFrameworkQuery::Reset(void) { // Clear out any old values m_sQuery.Empty(); m_sQueryFormat.Empty(); m_bKeysOnly = false; m_AddKeys = true; m_csaPropertiesRequired.RemoveAll(); if (m_pLevel1RPNExpression) { delete m_pLevel1RPNExpression; m_pLevel1RPNExpression = NULL; } if (m_IClass) { m_IClass->Release(); m_IClass = NULL; } } /***************************************************************************** * * FUNCTION : IsReference * * DESCRIPTION : Determines whether the specified property is a reference * property. * * INPUTS : * * OUTPUTS : * * RETURNS : * * COMMENTS : * *****************************************************************************/ BOOL CFrameworkQuery::IsReference( LPCWSTR lpwszPropertyName ) { BOOL bRet = FALSE; if (m_IClass != NULL) { CIMTYPE ctCimType; if (SUCCEEDED(m_IClass->Get(lpwszPropertyName, 0, NULL, &ctCimType, NULL))) { bRet = ctCimType == CIM_REFERENCE; } } return bRet; } /***************************************************************************** * * FUNCTION : GetNamespace * * DESCRIPTION : Determines whether the specified property is a reference * property. * * INPUTS : * * OUTPUTS : * * RETURNS : * * COMMENTS : * *****************************************************************************/ const CHString &CFrameworkQuery::GetNamespace() { return m_sNamespace; };