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

748 lines
22 KiB

/*++
Copyright (C) 1999-2001 Microsoft Corporation
Module Name:
PROTOQ.CPP
Abstract:
Prototype query support for WinMgmt Query Engine.
This was split out from QENGINE.CPP for better source
organization.
History:
raymcc 04-Jul-99 Created.
--*/
#include "precomp.h"
#include <stdio.h>
#include <stdlib.h>
#include <wbemcore.h>
int SelectedClass::SetAll(int & nPos)
{
m_bAll = TRUE;
// For each property, add an entry
CWbemClass *pCls = (CWbemClass *)m_pClassDef;
pCls->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY);
OnDeleteObj0<IWbemClassObject,
HRESULT(__stdcall IWbemClassObject:: *)(void),
IWbemClassObject::EndEnumeration> EndMe(pCls);
BSTR PropName = 0;
while (S_OK == pCls->Next(0, &PropName, NULL, NULL, NULL))
{
CSysFreeMe smf(PropName);
int nRes = SetNamed(PropName, nPos);
if (CFlexArray::no_error != nRes)
return nRes;
}
return CFlexArray::no_error;
};
HRESULT ReleaseClassDefs(IN CFlexArray *pDefs)
{
for (int i = pDefs->Size()-1; i >= 0 ; i--)
{
SelectedClass *pSelClass = (SelectedClass *) pDefs->GetAt(i);
delete pSelClass;
}
return WBEM_NO_ERROR;
}
//***************************************************************************
//
// ExecPrototypeQuery
//
// Called by CQueryEngine::ExecQuery for SMS-style prototypes.
//
// Executes the query and returns only the class definition implied
// by the query, whether a join or a simple class def.
//
//***************************************************************************
HRESULT ExecPrototypeQuery(
IN CWbemNamespace *pNs,
IN LPWSTR pszQuery,
IN IWbemContext* pContext,
IN CBasicObjectSink *pSink
)
{
HRESULT hRes;
int nRes;
if (pSink == NULL) return WBEM_E_INVALID_PARAMETER;
if (pNs == NULL )
return pSink->Return(WBEM_E_INVALID_PARAMETER);
// Parse the query and determine if it is a single class.
// ======================================================
CTextLexSource src(pszQuery);
CWQLScanner Parser(&src);
nRes = Parser.Parse();
if (nRes != CWQLScanner::SUCCESS)
return pSink->Return(WBEM_E_INVALID_QUERY);
// If a single class definition, branch, since we don't
// want to create a __GENERIC object.
// ====================================================
CWStringArray aAliases;
Parser.GetReferencedAliases(aAliases);
if (aAliases.Size() == 1)
{
LPWSTR pszClass = Parser.AliasToTable(aAliases[0]);
return GetUnaryPrototype(Parser, pszClass, aAliases[0], pNs, pContext, pSink);
}
// If here, a join must have occurred.
// ===================================
CFlexArray aClassDefs;
OnDelete<CFlexArray *,HRESULT(*)( CFlexArray *), ReleaseClassDefs > FreeMe(&aClassDefs);
hRes = RetrieveClassDefs(Parser,pNs, pContext,aAliases,&aClassDefs); // throw
if (FAILED(hRes)) return pSink->Return(WBEM_E_INVALID_QUERY);
// Iterate through all the properties selected.
// ============================================
const CFlexArray *pSelCols = Parser.GetSelectedColumns();
int nPosSoFar = 0;
for (int i = 0; i < pSelCols->Size(); i++)
{
SWQLColRef *pColRef = (SWQLColRef *) pSelCols->GetAt(i);
hRes = SelectColForClass(Parser, &aClassDefs, pColRef, nPosSoFar);
if (hRes) return pSink->Return(hRes);
}
// If here, we have the class definitions.
// =======================================
IWbemClassObject *pProtoInst = 0;
hRes = AdjustClassDefs(&aClassDefs, &pProtoInst);
CReleaseMe rmProto(pProtoInst);
if (hRes) return pSink->Return(hRes);
if (FAILED(hRes = pSink->Add(pProtoInst))) return pSink->Return(hRes);
return pSink->Return(WBEM_NO_ERROR);
}
//***************************************************************************
//
//***************************************************************************
HRESULT RetrieveClassDefs(
IN CWQLScanner & Parser,
IN CWbemNamespace *pNs,
IN IWbemContext *pContext,
IN CWStringArray & aAliasNames,
OUT CFlexArray *pDefs
)
{
for (int i = 0; i < aAliasNames.Size(); i++)
{
// Retrieve the class definition.
LPWSTR pszClass = Parser.AliasToTable(aAliasNames[i]);
if (pszClass == 0)
continue;
IWbemClassObject *pClassDef = 0;
HRESULT hRes = pNs->Exec_GetObjectByPath(pszClass, 0, pContext,&pClassDef, 0);
CReleaseMe rmClassDef(pClassDef);
if (FAILED(hRes)) return hRes;
wmilib::auto_ptr<SelectedClass> pSelClass( new SelectedClass);
if (NULL == pSelClass.get())
return WBEM_E_OUT_OF_MEMORY;
pSelClass->m_wsClass = pszClass; // throw
pSelClass->m_wsAlias = aAliasNames[i]; // throw
pClassDef->AddRef();
pSelClass->m_pClassDef = pClassDef;
if (CFlexArray::no_error != pDefs->Add(pSelClass.get())) return WBEM_E_OUT_OF_MEMORY;
pSelClass.release();
}
return WBEM_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
//***************************************************************************
//
//***************************************************************************
HRESULT SelectColForClass(
IN CWQLScanner & Parser,
IN CFlexArray *pClassDefs,
IN SWQLColRef *pColRef,
IN int & nPosition
)
{
int i;
HRESULT hRes;
if (!pColRef)
return WBEM_E_FAILED;
// If the column reference contains the class referenced
// via an alias and there is no asterisk, we are all set.
// ======================================================
if (pColRef->m_pTableRef)
{
// We now have the class name. Let's find it and add
// the referenced column for that class!
// =================================================
for (i = 0; i < pClassDefs->Size(); i++)
{
SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
if (wbem_wcsicmp(LPWSTR(pSelClass->m_wsAlias), pColRef->m_pTableRef) != 0)
continue;
CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
// See if the asterisk was used for this class.
// =============================================
if (pColRef->m_pColName[0] == L'*' && pColRef->m_pColName[1] == 0)
{
pSelClass->SetAll(nPosition);
return WBEM_NO_ERROR;
}
// If here, a property was mentioned by name.
// Verify that it exists.
// ==========================================
CVar Prop;
hRes = pCls->GetProperty(pColRef->m_pColName, &Prop);
if (FAILED(hRes))
return WBEM_E_INVALID_QUERY;
// Mark it as seleted.
// ===================
if (CFlexArray::no_error != pSelClass->SetNamed(pColRef->m_pColName, nPosition))
return WBEM_E_OUT_OF_MEMORY;
return WBEM_NO_ERROR;
}
// If here, we couldn't locate the property in any class.
// ======================================================
return WBEM_E_INVALID_QUERY;
}
// Did we select * from all tables?
// ================================
if (pColRef->m_dwFlags & WQL_FLAG_ASTERISK)
{
for (i = 0; i < pClassDefs->Size(); i++)
{
SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
if (CFlexArray::no_error != pSelClass->SetAll(nPosition))
return WBEM_E_OUT_OF_MEMORY;
}
return WBEM_NO_ERROR;
}
// If here, we have an uncorrelated property and we have to find out
// which class it belongs to. If it belongs to more than one, we have
// an ambiguous query.
// ===================================================================
DWORD dwTotalMatches = 0;
for (i = 0; i < pClassDefs->Size(); i++)
{
SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
// Try to locate the property in this class.
// =========================================
CVar Prop;
hRes = pCls->GetProperty(pColRef->m_pColName, &Prop);
if (hRes == 0)
{
if (CFlexArray::no_error != pSelClass->SetNamed(pColRef->m_pColName, nPosition))
return WBEM_E_OUT_OF_MEMORY;
dwTotalMatches++;
}
}
// If more than one match occurred, we have an ambiguous query.
// ============================================================
if (dwTotalMatches != 1)
return WBEM_E_INVALID_QUERY;
return WBEM_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
HRESULT AddOrderQualifiers(
CWbemClass *pCls,
BSTR PropName,
CFlexArray Matches
)
{
IWbemQualifierSet * pQual;
SCODE sc = pCls->GetPropertyQualifierSet(PropName, &pQual);
if(sc != S_OK)
return sc;
CReleaseMe rm(pQual);
// Create a safe array
SAFEARRAYBOUND aBounds[1];
aBounds[0].lLbound = 0;
aBounds[0].cElements = Matches.Size();
SAFEARRAY* pArray = SafeArrayCreate(VT_I4, 1, aBounds);
if (NULL == pArray) return WBEM_E_OUT_OF_MEMORY;
// Stuff the individual data pieces
// ================================
for(int nIndex = 0; nIndex < Matches.Size(); nIndex++)
{
long lPos = PtrToLong(Matches.GetAt(nIndex));
sc = SafeArrayPutElement(pArray, (long*)&nIndex, &lPos);
}
VARIANT var;
var.vt = VT_ARRAY | VT_I4;
var.parray = pArray;
sc = pQual->Put(L"Order", &var, 0);
VariantClear(&var);
return sc;
}
//***************************************************************************
//
//***************************************************************************
HRESULT SetPropertyOrderQualifiers(SelectedClass *pSelClass)
{
HRESULT hRes = S_OK;
CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
// Go through each property
pCls->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY);
OnDeleteObj0<IWbemClassObject,
HRESULT(__stdcall IWbemClassObject:: *)(void),
IWbemClassObject::EndEnumeration> EndMe(pCls);
BSTR PropName = 0;
while (S_OK == pCls->Next(0, &PropName, NULL, NULL, NULL))
{
CSysFreeMe sfm(PropName);
// Build up a list of properties that Match
CFlexArray Matches;
bool bAtLeastOne = false;
for(int iCnt = 0; iCnt < pSelClass->m_aSelectedCols.Size(); iCnt++)
{
if(!wbem_wcsicmp(pSelClass->m_aSelectedCols.GetAt(iCnt), PropName))
{
if (CFlexArray::no_error == Matches.Add(pSelClass->m_aSelectedColsPos.GetAt(iCnt)))
{
bAtLeastOne = true;
}
}
}
if(bAtLeastOne)
{
hRes = AddOrderQualifiers(pCls, PropName, Matches);
if (FAILED(hRes)) return hRes;
}
}
return hRes;
}
//***************************************************************************
//
// AdjustClassDefs
//
// After all class definitions have been retrieved, they are adjusted
// to only have the properties required and combined into a __GENERIC
// instance.
//
//***************************************************************************
HRESULT AdjustClassDefs(
IN CFlexArray *pClassDefs,
OUT IWbemClassObject **pRetNewClass
)
{
int i;
HRESULT hRes;
for (i = 0; i < pClassDefs->Size(); i++)
{
SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
if (pSelClass->m_bAll)
{
hRes = SetPropertyOrderQualifiers(pSelClass);
if (FAILED(hRes)) return hRes;
continue;
}
WString wsError = pCls->FindLimitationError(0, &pSelClass->m_aSelectedCols);
if (wsError.Length() > 0)
return WBEM_E_FAILED;
// Map the limitaiton
// ==================
CLimitationMapping Map;
BOOL bValid = pCls->MapLimitation(0, &pSelClass->m_aSelectedCols, &Map);
if (!bValid)
return WBEM_E_FAILED;
CWbemClass* pStrippedClass = 0;
hRes = pCls->GetLimitedVersion(&Map, &pStrippedClass);
if(SUCCEEDED(hRes))
{
pSelClass->m_pClassDef = pStrippedClass;
pCls->Release();
if (FAILED(hRes = SetPropertyOrderQualifiers(pSelClass))) return hRes;
}
}
// Count the number of objects that actually have properties
int iNumObj = 0;
for (i = 0; i < pClassDefs->Size(); i++)
{
SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
CWbemObject *pObj = (CWbemObject *) pSelClass->m_pClassDef;
if (pObj->GetNumProperties() > 0)
iNumObj++;
}
// If there is just one object with properties, return it rather than the generic object
if(iNumObj == 1)
{
for (i = 0; i < pClassDefs->Size(); i++)
{
SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
CWbemObject *pObj = (CWbemObject *) pSelClass->m_pClassDef;
if (pObj->GetNumProperties() == 0)
continue;
// Return it.
// ==========
*pRetNewClass = pObj;
pObj->AddRef();
return WBEM_NO_ERROR;
}
}
// Prepare a __GENERIC class def. We construct a dummy definition which
// has properties named for each of the aliases used in the query.
// =====================================================================
CGenericClass *pNewClass = new CGenericClass;
if (pNewClass == 0) return WBEM_E_OUT_OF_MEMORY;
CReleaseMe rmNewCls((IWbemClassObject *)pNewClass);
pNewClass->Init(); // throw
for (i = 0; i < pClassDefs->Size(); i++)
{
SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
CWbemObject *pObj = (CWbemObject *) pSelClass->m_pClassDef;
if (pObj->GetNumProperties() == 0)
continue;
CVar vEmbeddedClass;
vEmbeddedClass.SetAsNull();
if (FAILED( hRes = pNewClass->SetPropValue(pSelClass->m_wsAlias, &vEmbeddedClass, CIM_OBJECT))) return hRes;
CVar vClassName;
if (FAILED(hRes = pObj->GetClassName(&vClassName))) return hRes;
WString wsCimType = L"object:";
wsCimType += vClassName.GetLPWSTR();
CVar vCimType(VT_BSTR, wsCimType);
if (FAILED( hRes = pNewClass->SetPropQualifier(pSelClass->m_wsAlias, L"cimtype", 0,&vCimType))) return hRes;
};
// Spawn an instance of this class.
// ================================
CWbemInstance* pProtoInst = 0;
if (FAILED( hRes = pNewClass->SpawnInstance(0, (IWbemClassObject **) &pProtoInst))) return hRes;
CReleaseMe rmProtInst((IWbemClassObject *)pProtoInst);
rmNewCls.release();
// Now assign the properties to the embedded instances.
// ====================================================
for (i = 0; i < pClassDefs->Size(); i++)
{
SelectedClass *pSelClass = (SelectedClass *) pClassDefs->GetAt(i);
CWbemClass *pCls = (CWbemClass *) pSelClass->m_pClassDef;
if (pCls->GetNumProperties() == 0)
continue;
CVar vEmbedded;
vEmbedded.SetEmbeddedObject((IWbemClassObject *) pCls);
if (FAILED( hRes = pProtoInst->SetPropValue(pSelClass->m_wsAlias, &vEmbedded, 0))) return hRes;
};
// Return it.
// ==========
rmProtInst.dismiss();
*pRetNewClass = pProtoInst;
return WBEM_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
HRESULT GetUnaryPrototype(
IN CWQLScanner & Parser,
IN LPWSTR pszClass,
IN LPWSTR pszAlias,
IN CWbemNamespace *pNs,
IN IWbemContext *pContext,
IN CBasicObjectSink *pSink
)
{
int i;
// Retrieve the class definition.
// ==============================
IWbemClassObject *pClassDef = 0;
IWbemClassObject *pErrorObj = 0;
HRESULT hRes = pNs->Exec_GetObjectByPath(pszClass, 0, pContext,&pClassDef, &pErrorObj);
CReleaseMeRef<IWbemClassObject *> rmObj(pClassDef);
CReleaseMe rmErrObj(pErrorObj);
if (FAILED(hRes))
{
pSink->SetStatus(0, hRes, NULL, pErrorObj);
return S_OK;
}
rmErrObj.release();
CWbemClass *pCls = (CWbemClass *) pClassDef;
BOOL bKeepAll = FALSE;
// This keeps track of the order in which columns are selected
SelectedClass sel;
sel.m_wsClass = pszClass; // throw
sel.m_pClassDef = pClassDef;
pClassDef->AddRef();
// Go through all the columns and make sure that the properties are valid
// ======================================================================
const CFlexArray *pSelCols = Parser.GetSelectedColumns();
int nPosition = 0;
for (i = 0; i < pSelCols->Size(); i++)
{
SWQLColRef *pColRef = (SWQLColRef *) pSelCols->GetAt(i);
if (pColRef->m_dwFlags & WQL_FLAG_ASTERISK)
{
bKeepAll = TRUE;
if (CFlexArray::no_error == sel.SetAll(nPosition))
continue;
else
return pSink->Return(WBEM_E_FAILED);
}
if (pColRef->m_pColName)
{
// check for the "select x.* from x" case
if(pColRef->m_pColName[0] == L'*' && pColRef->m_pColName[1] == 0)
{
if (!wbem_wcsicmp(pColRef->m_pTableRef, pszAlias)) // SEC:REVIEWED 2002-03-22 : OK, prior guarantee of NULL terminators
{
bKeepAll = TRUE;
if (CFlexArray::no_error == sel.SetAll(nPosition))
continue;
else
return pSink->Return(WBEM_E_FAILED);
continue;
}
else
{
return pSink->Return(WBEM_E_INVALID_QUERY);
}
}
// Verify that the class has it
// ============================
CIMTYPE ct;
if(FAILED(pCls->GetPropertyType(pColRef->m_pColName, &ct)))
{
// No such property
// ================
return pSink->Return(WBEM_E_INVALID_QUERY);
}
if (CFlexArray::no_error != sel.SetNamed(pColRef->m_pColName, nPosition))
return pSink->Return(WBEM_E_FAILED);
}
}
// Eliminate unreferenced columns from the query.
// ==============================================
CWStringArray aPropsToKeep; // SEC:REVIEWED 2002-03-22 : May throw
if(!bKeepAll)
{
// Move through each property in the class and
// see if it is referenced. If not, remove it.
// ============================================
int nNumProps = pCls->GetNumProperties();
for (i = 0; i < nNumProps; i++)
{
CVar Prop;
HRESULT hrInner;
hrInner = pCls->GetPropName(i, &Prop);
if (FAILED(hrInner)) return pSink->Return(hrInner);
// See if this name is used in the query.
// ======================================
for (int i2 = 0; i2 < pSelCols->Size(); i2++)
{
SWQLColRef *pColRef = (SWQLColRef *) pSelCols->GetAt(i2);
if (pColRef->m_pColName && wbem_wcsicmp(Prop, pColRef->m_pColName) == 0)
{
if (CFlexArray::no_error != aPropsToKeep.Add((LPWSTR) Prop))
return pSink->Return(WBEM_E_FAILED);
break;
}
}
}
}
// Now we have a list of properties to remove.
// ===========================================
if (!bKeepAll && aPropsToKeep.Size())
{
WString wsError = pCls->FindLimitationError(0, &aPropsToKeep); // throw
if (wsError.Length() > 0)
{
return pSink->Return(WBEM_E_FAILED);
}
// Map the limitaiton
// ==================
CLimitationMapping Map;
BOOL bValid = pCls->MapLimitation(0, &aPropsToKeep, &Map);
if (!bValid)
{
return pSink->Return(WBEM_E_FAILED);
}
CWbemClass* pNewStrippedClass = 0;
hRes = pCls->GetLimitedVersion(&Map, &pNewStrippedClass);
if(SUCCEEDED(hRes))
{
// this is for the on-stack object
pClassDef->Release();
// this is for the copy given to the SelectClass object
sel.m_pClassDef->Release();
sel.m_pClassDef = pNewStrippedClass;
pNewStrippedClass->AddRef();
pClassDef = pNewStrippedClass; // give ownership to the oure scope
}
}
// Add the Order qualifier
hRes= SetPropertyOrderQualifiers(&sel);
if (FAILED(hRes)) return pSink->Return(hRes);
// Return it.
// ==========
hRes = pSink->Add(pClassDef);
if (FAILED(hRes)) return pSink->Return(hRes);
return pSink->Return(WBEM_NO_ERROR);
}