//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2000. // // File: umisrch.cxx // // Contents: This object wraps an IDirectorySearch and returns Umi objects // as a result of the search. The Umi objects returned are // pre-populated with the attributes received from the search. // This object is used by the cursor object to support the // IUmiCursor interface. // This file also contains the CQueryStack helper class that is // used by the SearcHelper to parse SQL queries. // // History: 03-20-00 AjayR Created. // //---------------------------------------------------------------------------- #include "ldap.hxx" // // Helper functions. // //+--------------------------------------------------------------------------- // Function: HelperGetAttributeList - helper routine. // // Synopsis: Gets a list of attributes that can be used in an execute // search call from the wbem query information. // // Arguments: pdwAttribCount - Returns the number of attributes. // -1 means all attributes. // pppszAttribArray - Return value for string array. // ulListSize - Size of list of wbem names. // pWbemNames - The list of parsed tokens. // // Returns: S_OK or any appropriate error code. // // Modifies: pdwAttribCount to correct value and pppszAttribArray to // array of strings containing the attribute list. // //---------------------------------------------------------------------------- HRESULT HelperGetAttributeList( PDWORD pdwAttribCount, LPWSTR ** pppszAttribArray, ULONG ulListSize, SWbemQueryQualifiedName **pWbemNames ) { HRESULT hr = S_OK; DWORD dwCtr = 0; SWbemQueryQualifiedName *pName = NULL; LPWSTR pszTmpString = NULL; // // There should at least be a * in the list. // ADsAssert(pWbemNames); *pppszAttribArray = NULL; *pdwAttribCount = (DWORD) -1; // // If the count is just one we need to see if this is just *. // if (ulListSize == 1) { pName = pWbemNames[0]; if (pName->m_ppszNameList && pName->m_ppszNameList[0] ) { if (_wcsicmp(pName->m_ppszNameList[0], L"*")) { // // We need to retun NULL and -1 for count. // RRETURN(hr); } } else { // // This has to be a bad query cause either // 1) the name list was empty - cannot have empty from clause, // 2) the name list had an empty string in it, or // BAIL_ON_FAILURE(hr = E_FAIL); // UMI_E_UNSUPPORTED_QUERY ??? } } // // At this point we have a valid attribute list. // *pppszAttribArray = (LPWSTR *) AllocADsMem(sizeof(LPWSTR) * ulListSize); if (!*pppszAttribArray) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } for (dwCtr = 0; dwCtr < ulListSize; dwCtr++) { pszTmpString = NULL; pName = pWbemNames[dwCtr]; if (!pName || !pName->m_ppszNameList) { BAIL_ON_FAILURE(hr = E_FAIL); // UMI_E_BAD_QUERY ??? } // // ADSI does not expect more than one entry in each of the pName's. // so if some did user.Description then we just treat this as // as description by using the last value always. // pszTmpString = AllocADsStr( pName->m_ppszNameList[pName->m_uNameListSize-1] ); if (!pszTmpString) { BAIL_ON_FAILURE(hr = E_FAIL); } (*pppszAttribArray)[dwCtr] = pszTmpString; } *pdwAttribCount = ulListSize; error: if (FAILED(hr)) { if (*pppszAttribArray) { for (dwCtr = 0; dwCtr < ulListSize; dwCtr++) { pszTmpString = (*pppszAttribArray)[dwCtr]; if (pszTmpString) { FreeADsStr(pszTmpString); } } FreeADsMem(*pppszAttribArray); *pppszAttribArray = NULL; } } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: HelperGetFromList - helper routine. // // Synopsis: Get a filter that represents the from list. For now this // will only return NULL. However we will need to spruce this fn // if we need to support a list of classes in here. // // Arguments: ppszFromFilter - Return value for a filter corresponding // to the From list. // pQuery - Wbem query object. // // Returns: S_OK or any appropriate error code. // // Modifies: *ppszFromFilter if From list is valid. // //---------------------------------------------------------------------------- HRESULT HelperGetFromList( LPWSTR *ppszFromFilter, SWbemRpnEncodedQuery *pQuery ) { HRESULT hr = E_FAIL; LPWSTR pszFilter = NULL; *ppszFromFilter = NULL; if (pQuery->m_uFromTargetType & WMIQ_RPN_FROM_PATH) { // // This can only be . for now. // if (!_wcsicmp(L".", pQuery->m_pszOptionalFromPath)) { // // We are ok as long as there are no other pieces. // RRETURN(S_OK); } else { RRETURN(E_FAIL); // UMI_E_NOT_SUPPORTED ??? } } // // For now we are going to ignore pieces if there are any other // pieces in the from clause. // if (pQuery->m_uFromTargetType & WMIQ_RPN_FROM_UNARY) { // hr = E_FAIL; UMI_E_NOT_SUPPORTED. hr = S_OK; } else if (pQuery->m_uFromTargetType & WMIQ_RPN_FROM_CLASS_LIST) { // hr = E_FAIL; UMI_E_NOT_SUPPORTED. //for (unsigned u = 0; u < pQuery->m_uFromListSize; u++) //{ // printf(" Selected class = %S\n", pQuery->m_ppszFromList[u]); //} hr = S_OK; } else { // // The query does not have anything in the from clause. // This is an invalid query. // hr = E_FAIL; // UMI_E_NOT_SUPPORTED ??? } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: HelperGetSubExpressionFromOperands - helper routine. // // Synopsis: Updates the result with the operands and the operator. This // routine munges the operators/operands around so that we can // support operands not supported by ldap directly. // // Arguments: pszResult - Return value, allocated by caller. // pszOperand1 - The attribute being tested. // pszOperand2 - The value being tested against. // ulOperator - Inidcates the comparision type. // // Returns: S_OK or any appropriate error code. // // Modifies: pStack contents. // //---------------------------------------------------------------------------- HRESULT HelperGetSubExpressionFromOperands( LPWSTR pszResult, LPWSTR pszOperand1, LPWSTR pszOperand2, ULONG ulOperator ) { HRESULT hr = S_OK; // // In all cases we will need to begin with "(". // wsprintf(pszResult, L"("); // // All these are easy to handle. // if ((ulOperator == WMIQ_RPN_OP_EQ) || (ulOperator == WMIQ_RPN_OP_LT) || (ulOperator == WMIQ_RPN_OP_GT) ) { wcscat(pszResult, pszOperand1); } switch (ulOperator) { case WMIQ_RPN_OP_EQ : wcscat(pszResult, L"="); wcscat(pszResult, pszOperand2); wcscat(pszResult, L")"); break; case WMIQ_RPN_OP_LT : wcscat(pszResult, L"<"); wcscat(pszResult, pszOperand2); wcscat(pszResult, L")"); break; case WMIQ_RPN_OP_GT : wcscat(pszResult, L">"); wcscat(pszResult, pszOperand2); wcscat(pszResult, L")"); break; // // All these need some processing as ldap does not // handle these directly. // case WMIQ_RPN_OP_NE : // // This needs to be (!(operand1=operand2)) so a != b becomes // (!(a=b)) // wcscat(pszResult, L"!"); wcscat(pszResult, L"("); wcscat(pszResult, pszOperand1); wcscat(pszResult, L"="); wcscat(pszResult, pszOperand2); wcscat(pszResult, L"))"); break; case WMIQ_RPN_OP_GE : // // a >= b becomes (!(ab)) // wcscat(pszResult, L"!("); wcscat(pszResult, pszOperand1); wcscat(pszResult, L">"); wcscat(pszResult, pszOperand2); wcscat(pszResult, L"))"); break; default: hr = E_FAIL; // UMI_E_UNSUPPORTED_QUERY. break; } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: HelperProcessOperator - helper routine. // // Synopsis: Pops 2 elements from the stack, applies the operator on them // and pushes the effective expression on to the stack. // // Arguments: pStack - Valid stack which should have at least 2 // elements in the stack. // ulOperatorType- Operator type corresponding to wbem enum. // // Returns: S_OK or any appropriate error code. // // Modifies: pStack contents. // //---------------------------------------------------------------------------- HRESULT HelperProcessSubExpression( CQueryStack *pStack, SWbemRpnQueryToken *pExpression ) { HRESULT hr = S_OK; LPWSTR pszOperand1 = NULL; LPWSTR pszOperand2 = NULL; LPWSTR pszResult = NULL; ULONG ulShape = pExpression->m_uSubexpressionShape; DWORD dwTotalLen = 0; ADsAssert(pStack); // // As per what is supported we should not have a function. // However the query parses seems to always report functions so we // will ignore. // if (ulShape & WMIQ_RPN_LEFT_PROPERTY_NAME) { SWbemQueryQualifiedName *pName = pExpression->m_pLeftIdent; // // The identifier could be x.y.z in which case ADSI is only // interested in z // if (pName->m_ppszNameList[pName->m_uNameListSize-1]) { // // Alloc the name itself if the operand1 is not __CLASS. // If it is __CLASS we need to substitute with objectClass. // if (!_wcsicmp( pName->m_ppszNameList[pName->m_uNameListSize-1], L"__CLASS" ) ) { pszOperand1 = AllocADsStr(L"objectClass"); } else { // // Alloc whatever was passed in. // pszOperand1 = AllocADsStr( pName->m_ppszNameList[pName->m_uNameListSize-1] ); } if (!pszOperand1) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } } else { // // Something really wrong. // BAIL_ON_FAILURE(hr = E_FAIL); } } else { // // We cannot handle this as there is no identifier on LHS. // BAIL_ON_FAILURE(hr = E_FAIL); } // // We should have the operand by now, need the value // switch (pExpression->m_uConstApparentType) { case VT_I4: case VT_UI4: // // We cannot possibly have more than 20 chars. // pszOperand2 = (LPWSTR) AllocADsMem(20 * sizeof(WCHAR)); if (!pszOperand2) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } if (pExpression->m_uConstApparentType == VT_I4) { _ltot(pExpression->m_Const.m_lLongVal, pszOperand2, 10 ); } else { _ltot(pExpression->m_Const.m_uLongVal, pszOperand2, 10); } break; case VT_BOOL: // // Again 20 sounds good ! // pszOperand2 = (LPWSTR) AllocADsMem(20 * sizeof(WCHAR)); if (!pszOperand2) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } if (pExpression->m_Const.m_bBoolVal ) wcscpy(pszOperand2, L"TRUE"); else wcscpy(pszOperand2, L"FALSE"); break; case VT_LPWSTR: pszOperand2 = AllocADsStr(pExpression->m_Const.m_pszStrVal); if (!pszOperand2) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } break; default: BAIL_ON_FAILURE(hr = E_FAIL); // UMI_E_UNSUPPORTED. break; } // // At this point we can combine the 2 operands and operator. // Total size is size of strings + () + 2 for operator + 1 for \0. // Total additions = 5. This is for most cases. // dwTotalLen = wcslen(pszOperand1) + wcslen(pszOperand2) + 5; // // Operators <= >= and != need to be processed to be usable in ldap. // In these cases, we assume that twice the length is needed. // dwTotalLen *= 2; pszResult = (LPWSTR) AllocADsMem(dwTotalLen * sizeof(WCHAR)); if (!pszResult) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } hr = HelperGetSubExpressionFromOperands( pszResult, pszOperand1, pszOperand2, pExpression->m_uOperator ); /* wsprintf(pszResult, L"("); wcscat(pszResult, pszOperand1); // // Need to add the opearator now. // switch (pExpression->m_uOperator) { case WMIQ_RPN_OP_EQ : wcscat(pszResult,L"="); break; case WMIQ_RPN_OP_NE : wcscat(pszResult, L"!="); break; case WMIQ_RPN_OP_GE : wcscat(pszResult, L">="); break; case WMIQ_RPN_OP_LE : wcscat(pszResult, L"<="); break; case WMIQ_RPN_OP_LT : wcscat(pszResult, L"<"); break; case WMIQ_RPN_OP_GT : wcscat(pszResult, L">"); break; case WMIQ_RPN_OP_UNDEFINED: case WMIQ_RPN_OP_LIKE : case WMIQ_RPN_OP_ISA : case WMIQ_RPN_OP_ISNOTA : default : // // All these mean we have a bad query. // BAIL_ON_FAILURE(hr = E_FAIL); break; } // // Now tag on operand2 and the closing ), and push result onto stack. // wcscat(pszResult, pszOperand2); wcscat(pszResult, L")"); */ hr = pStack->Push(pszResult, QUERY_STACK_ITEM_LITERAL); BAIL_ON_FAILURE(hr); error: if (pszOperand1) { FreeADsStr(pszOperand1); } if (pszOperand2) { FreeADsStr(pszOperand2); } if (pszResult) { FreeADsStr(pszResult); } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: HelperProcessOperator - helper routine. // // Synopsis: Pops 2 elements from the stack, applies the operator on them // and pushes the effective expression on to the stack. // // Arguments: pStack - Valid stack which should have at least 2 // elements in the stack. // ulOperatorType- Operator type corresponding to wbem enum. // // Returns: S_OK or any appropriate error code. // // Modifies: pStack contents. // //---------------------------------------------------------------------------- HRESULT HelperProcessOperator( CQueryStack *pStack, ULONG ulOperatorType ) { HRESULT hr; LPWSTR pszStr1 = NULL; LPWSTR pszStr2 = NULL; LPWSTR pszResult = NULL; DWORD dwNodeType; DWORD dwTotalLen = 0; ADsAssert(pStack); hr = pStack->Pop( &pszStr1, &dwNodeType ); BAIL_ON_FAILURE(hr); // // String has to be valid and the node cannot be an operator. // if (!pszStr1 || !*pszStr1 || (dwNodeType != QUERY_STACK_ITEM_LITERAL) ) { BAIL_ON_FAILURE(hr = E_FAIL); // UMI_E_UNSUPPORTED_QUERY } dwTotalLen += wcslen(pszStr1); // // Not has only one operand. // if (ulOperatorType != WMIQ_RPN_TOKEN_NOT) { hr = pStack->Pop( &pszStr2, &dwNodeType ); BAIL_ON_FAILURE(hr); // // Sanity check. // if (!pszStr2 || !*pszStr2 || (dwNodeType != QUERY_STACK_ITEM_LITERAL) ) { BAIL_ON_FAILURE(hr = E_FAIL); // UMI_E_UNSUPPORTED_QUERY } // // Need to add string2's length. // dwTotalLen += wcslen(pszStr2); } // // The resultant node needs to hold both strings and the operator // and the additional set of parentheses. // 4 = operator + () and one for \0. // dwTotalLen += 4; pszResult = (LPWSTR) AllocADsMem(dwTotalLen * sizeof(WCHAR)); if (!pszResult) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } wsprintf(pszResult, L"("); switch (ulOperatorType) { case WMIQ_RPN_TOKEN_AND : wcscat(pszResult, L"&"); break; case WMIQ_RPN_TOKEN_OR : wcscat(pszResult, L"|"); break; case WMIQ_RPN_TOKEN_NOT : wcscat(pszResult, L"!"); break; default: BAIL_ON_FAILURE(hr = E_FAIL); // UMI_E_UNSUPPORTED. } // // At this point we need to tag on both the pre composed pieces // and the final closing parentheses. // wcscat(pszResult, pszStr1); // // Do not Tag on the second string only if operator is NOT. // if (ulOperatorType != WMIQ_RPN_TOKEN_NOT) { wcscat(pszResult, pszStr2); } wcscat(pszResult, L")"); // // Need to push the result on to the stack as a literal. // hr = pStack->Push( pszResult, QUERY_STACK_ITEM_LITERAL ); BAIL_ON_FAILURE(hr); error: if (pszStr1) { FreeADsStr(pszStr1); } if (pszStr2) { FreeADsStr(pszStr2); } if (pszResult) { FreeADsStr(pszResult); } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: HelperGetFilterFromWhereClause - helper routine. // // Synopsis: Returns a filter corresponding to the elements specified in // the where clause. // // Arguments: ppszFilter - Return value for a filter corresponding // to the Where clause. // ulCount - Number of elements in the where clause. // pWhere - Pointer to the Wbem where clause. // // Returns: S_OK or any appropriate error code. // // Modifies: *ppszFilter if Where clause is valid. // //---------------------------------------------------------------------------- HRESULT HelperGetFilterFromWhereClause( LPWSTR *ppszFilter, ULONG ulCount, SWbemRpnQueryToken **pWhere ) { HRESULT hr = S_OK; DWORD dwCtr; CQueryStack *pStack = NULL; LPWSTR pszFilter = NULL; DWORD dwType; *ppszFilter = NULL; if (!ulCount) { // // In this case we will default to NULL. // RRETURN(hr); } pStack = new CQueryStack(); if (!pStack) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } // // Process the tokens in the where clause. // for (dwCtr = 0; dwCtr < ulCount; dwCtr++) { SWbemRpnQueryToken *pToken = pWhere[dwCtr]; switch (pToken->m_uTokenType) { // // All these are operations. // case WMIQ_RPN_TOKEN_AND : case WMIQ_RPN_TOKEN_OR : case WMIQ_RPN_TOKEN_NOT : HelperProcessOperator(pStack, pToken->m_uTokenType); break; case WMIQ_RPN_TOKEN_EXPRESSION : HelperProcessSubExpression(pStack, pToken); break; default: // // We cannot handle the query. // hr = E_FAIL; // UMI_E_UNSUPPORTED_QUERY. break; } BAIL_ON_FAILURE(hr); } // // At this stack should have one element and that should be // the filter we use if things went right. // hr = pStack->Pop( &pszFilter, &dwType ); BAIL_ON_FAILURE(hr); if (!pStack->IsEmpty()) { BAIL_ON_FAILURE(hr = E_FAIL); // UMI_E_UNSUPPORTED_QUERY } *ppszFilter = pszFilter; error: if (pStack) { delete pStack; } if (FAILED(hr) && pszFilter) { FreeADsStr(pszFilter); } RRETURN(hr); } HRESULT HelperGetSortKey( SWbemRpnEncodedQuery *pRpn, PADS_SORTKEY * ppADsSortKey ) { HRESULT hr = S_OK; PADS_SORTKEY pSortKey = NULL; if (!pRpn->m_uOrderByListSize) { RRETURN(S_OK); } // // Allocate the sort key. // pSortKey = (PADS_SORTKEY) AllocADsMem(sizeof(ADS_SORTKEY)); if (!pSortKey) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } pSortKey->pszAttrType = AllocADsStr(pRpn->m_ppszOrderByList[0]); if (!pSortKey->pszAttrType) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } if (pRpn->m_uOrderDirectionEl[0]) { pSortKey->fReverseorder = TRUE; } RRETURN(S_OK); error: if (pSortKey) { if (pSortKey->pszAttrType) { FreeADsStr(pSortKey->pszAttrType); } FreeADsMem(pSortKey); } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::CUmiSearchHelper --- Constructor. // // Synopsis: Standard constructor. // // Arguments: N/A. // // Returns: N/A. // // Modifies: N/A. // //---------------------------------------------------------------------------- CUmiSearchHelper::CUmiSearchHelper(): _hSearchHandle(NULL), _pConnection(NULL), _pContainer(NULL), _pszADsPath(NULL), _pszLdapServer(NULL), _pszLdapDn(NULL), _pIID(NULL), _fSearchExecuted(FALSE), _fResetAllowed(TRUE) { // // This is global scope helper routine that will not fail. // The boolean is the value for cache results. // LdapInitializeSearchPreferences(&_searchPrefs, FALSE); } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::~CUmiSearchHelper --- Destructor. // // Synopsis: Standard destructor. // // Arguments: N/A. // // Returns: N/A. // // Modifies: N/A. // //---------------------------------------------------------------------------- CUmiSearchHelper::~CUmiSearchHelper() { if (_hSearchHandle) { ADsCloseSearchHandle(_hSearchHandle); } if (_pConnection) { _pConnection->Release(); } if (_pContainer) { _pContainer->Release(); } if (_pQuery) { _pQuery->Release(); } if (_pszADsPath) { FreeADsStr(_pszADsPath); } if (_pszLdapServer) { FreeADsStr(_pszLdapServer); } if (_pszLdapDn) { FreeADsStr(_pszLdapDn); } if (_pCreds) { delete _pCreds; } } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::CreateSearchHelper --- STATIC constructor. // // Synopsis: Static constructor. // // Arguments: pUmiQuery - Pointer to query object that. // pConnection - Connection to execute query on. // pUnk - Container on which the query is executed. // pSrchObj - New object is returned in this param. // // Returns: N/A. // // Modifies: N/A. // //---------------------------------------------------------------------------- HRESULT CUmiSearchHelper::CreateSearchHelper( IUmiQuery *pUmiQuery, IUmiConnection *pConnection, IUnknown *pUnk, LPCWSTR pszADsPath, LPCWSTR pszLdapServer, LPCWSTR pszLdapDn, CCredentials cCredentials, DWORD dwPort, CUmiSearchHelper FAR* FAR * ppSrchObj ) { HRESULT hr = S_OK; CUmiSearchHelper FAR * pSearchHelper = NULL; // // Asserts are good enough as this is an internal routine. // ADsAssert(ppSrchObj); ADsAssert(pUmiQuery && pConnection && pUnk); pSearchHelper = new CUmiSearchHelper(); if (!pSearchHelper) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } pSearchHelper->_pszADsPath = AllocADsStr(pszADsPath); if (!pSearchHelper->_pszADsPath) { BAIL_ON_FAILURE(hr); } // // pszLdapDn can be null // if (pszLdapDn) { pSearchHelper->_pszLdapDn = AllocADsStr(pszLdapDn); if (!pSearchHelper->_pszLdapDn) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } } // // the server can also be null. // if (pszLdapServer) { pSearchHelper->_pszLdapServer = AllocADsStr(pszLdapServer); if (!pSearchHelper->_pszLdapServer) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } } pSearchHelper->_pCreds = new CCredentials(cCredentials); if (!pSearchHelper->_pCreds) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } pSearchHelper->_pConnection = pConnection; pConnection->AddRef(); pSearchHelper->_pQuery = pUmiQuery; pUmiQuery->AddRef(); pSearchHelper->_pContainer = pUnk; pUnk->AddRef(); pSearchHelper->_dwPort = dwPort; *ppSrchObj = (CUmiSearchHelper FAR *)pSearchHelper; RRETURN(hr); error : delete pSearchHelper; RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::SetIID // // Synopsis: Sets the IID requested on the returned objects. // // Arguments: riid - The iid requested. // // Returns: S_OK or any appropriate error code. // // Modifies: _pIID the internal IID pointer. // //---------------------------------------------------------------------------- HRESULT CUmiSearchHelper::SetIID(REFIID riid) { if (_pIID) { FreeADsMem(_pIID); } _pIID = (IID *) AllocADsMem(sizeof(IID)); if (!_pIID) { RRETURN(E_OUTOFMEMORY); } memcpy(_pIID, &riid, sizeof(IID)); RRETURN(S_OK); } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::Reset // // Synopsis: Not implemented. // // Arguments: None. // // Returns: E_NOTIMPL. // // Modifies: N/A. // //---------------------------------------------------------------------------- HRESULT CUmiSearchHelper::Reset() { // // We can potentially add support for this down the road. // RRETURN(E_NOTIMPL); } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::Next // // Synopsis: Return the next uNumRequested elements from the search. // // Arguments: uNumRequested - number of objects/rows to fetch. // pNumReturned - number of objects/rows returned. // pObject - array of returned objects. // // Returns: S_OK, S_FALSE or any appropriate error code. // // Modifies: pNumReturned, pObjects and internal search handle state. // //---------------------------------------------------------------------------- HRESULT CUmiSearchHelper::Next( ULONG uNumRequested, ULONG *puNumReturned, LPVOID *ppObjects ) { HRESULT hr = S_OK; IUnknown **pTempUnks = NULL; DWORD dwCtr = 0, dwIndex = 0; if (!puNumReturned || !ppObjects || (uNumRequested < 1)) { BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER); } if (!_fSearchExecuted) { hr = InitializeSearchContext(); BAIL_ON_FAILURE(hr); } pTempUnks = (IUnknown **) AllocADsMem(sizeof(IUnknown*) * uNumRequested); if (!pTempUnks) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } // // Get the requested number of objects one at a time until // we hit S_FALSE or an error and then repackage to suitable // array as needed. // while ((dwCtr < uNumRequested) && (hr != S_FALSE) ) { IUnknown *pUnk = NULL; hr = GetNextObject(&pUnk); // // This wont catch S_FALSE. // BAIL_ON_FAILURE(hr); // // Needed as if he get S_FALSE we will set the ptr and // increase the count incorrectly. // if (hr != S_FALSE) { pTempUnks[dwCtr] = pUnk; dwCtr++; } } // // At this point we have all the results we need or can get. // if (uNumRequested == dwCtr) { // // We can return tempObjects in this case. // *ppObjects = (void *)pTempUnks; } else { // // Less than what had been requested. // IUnknown **pUnkCopy; pUnkCopy = (IUnknown **) AllocADsMem(sizeof(IUnknown *) * dwCtr); if (!pUnkCopy) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } for (dwIndex = 0; dwIndex < dwCtr; dwIndex++) { // // Just need to copy the ptrs over. // pUnkCopy[dwIndex] = pTempUnks[dwIndex]; } // // Only return the number of entries we got back. // *ppObjects = (void *) pUnkCopy; FreeADsMem((void *)pTempUnks); } // // We are error free over here. // *puNumReturned = dwCtr; RRETURN(hr); error: if (pTempUnks) { // // For will be executed only if we have valid count. // for (dwIndex = 0; dwIndex < dwCtr; dwIndex++) { pTempUnks[dwIndex]->Release(); } FreeADsMem(pTempUnks); } *puNumReturned = 0; RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::Previous (E_NOTIMPL) // // Synopsis: Return the previous object in the result set - Not implemented. // // Arguments: uFlags - only flag supported is 0. // pObj - object is returned in this param. // // Returns: S_OK, or any appropriate error code. // // Modifies: Internal search handle state and pObject. // //---------------------------------------------------------------------------- HRESULT CUmiSearchHelper::Previous( ULONG uFlags, LPVOID *pObj ) { RRETURN(E_NOTIMPL); } // // Internal/protected routines // //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::InitializeSearchContext. // // Synopsis: Prepares the internal state of the object. This initializes // the search preferences from the query object, leaveing the search // helper ready to retrieve results. // // Arguments: None. // // Returns: S_OK, or any appropriate error code. // // Modifies: Internal search preferences and also state search handle. // //---------------------------------------------------------------------------- HRESULT CUmiSearchHelper::InitializeSearchContext() { HRESULT hr = S_OK; PUMI_PROPERTY_VALUES pPropVals = NULL; IUmiPropList *pPropList = NULL; LPWSTR pszFilter = NULL; LPWSTR *pszAttrNames = NULL; DWORD dwAttrCount = (DWORD) -1; ULONG ulLangBufSize = 100 * sizeof(WCHAR); ULONG ulQueryBufSize = 1024 * sizeof(WCHAR); LPWSTR pszLangBuf = NULL; LPWSTR pszQueryText = NULL; PADS_SORTKEY pSortKey = NULL; // // We need to walk through the properties in the query and update // our local copy of preferences accordingly. // if (!_pQuery) { BAIL_ON_FAILURE(hr = E_FAIL); } hr = _pQuery->GetInterfacePropList(0, &pPropList); BAIL_ON_FAILURE(hr); // // Need to allocate memory for the buffers. // pszLangBuf = (LPWSTR) AllocADsMem(ulLangBufSize); pszQueryText = (LPWSTR) AllocADsMem(ulQueryBufSize); if (!pszLangBuf || !pszQueryText) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } // // We need to look at the query language if the language is SQL, // then we need to go through and convert the SQL settings to // properties on the intfPropList of the query. // hr = _pQuery->GetQuery( &ulLangBufSize, pszLangBuf, &ulQueryBufSize, // Change this before checking in. pszQueryText ); if (hr == E_OUTOFMEMORY ) { // // Means there was insufficient length in the buffers. // if (ulLangBufSize > (100 * sizeof(WCHAR))) { FreeADsStr(pszLangBuf); pszLangBuf = (LPWSTR) AllocADsMem(ulLangBufSize + sizeof(WCHAR)); if (!pszLangBuf) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } } if (ulQueryBufSize > (1024 * sizeof(WCHAR))) { FreeADsStr(pszQueryText); pszQueryText = (LPWSTR) AllocADsMem( ulQueryBufSize + sizeof(WCHAR) ); if (pszQueryText) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } } hr = _pQuery->GetQuery( &ulLangBufSize, pszLangBuf, &ulQueryBufSize, // Change this before checking in. pszQueryText ); } BAIL_ON_FAILURE(hr); if (*pszLangBuf) { if (!_wcsicmp(L"SQL", pszLangBuf) || !_wcsicmp(L"WQL", pszLangBuf) ) { // // This is a sql query and we need to process the query before // pulling the options out of the query object. // hr = ProcessSQLQuery( pszQueryText, &pszFilter, &pSortKey ); BAIL_ON_FAILURE(hr); } else if (!_wcsicmp(L"LDAP", pszLangBuf)) { // // Neither SQL nor LDAP but the language is set which means // we cannot handle this query. // pszFilter = AllocADsStr(pszQueryText); if (!pszFilter) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } } } // // At this point we need to go through and pick up all the properties. // we know about and update our search preferences accordingly. Each of // these properties has to be set and has to have some valid data. // // // Start with the search scope. // hr = pPropList->Get(L"__SEARCH_SCOPE", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidLongProperty(pPropVals)); _searchPrefs._dwSearchScope = pPropVals->pPropArray[0].pUmiValue->uValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Get the synchronous/asynchronous pref. // hr = pPropList->Get(L"__PADS_SEARCHPREF_ASYNCHRONOUS", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidBoolProperty(pPropVals)); _searchPrefs._fAsynchronous = pPropVals->pPropArray[0].pUmiValue->bValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Get the deref aliases search pref. // hr = pPropList->Get(L"__PADS_SEARCHPREF_DEREF_ALIASES", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidBoolProperty(pPropVals)); _searchPrefs._dwDerefAliases = pPropVals->pPropArray[0].pUmiValue->bValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Get the size limit. // hr = pPropList->Get(L"__PADS_SEARCHPREF_SIZE_LIMIT", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidLongProperty(pPropVals)); _searchPrefs._dwSizeLimit = pPropVals->pPropArray[0].pUmiValue->uValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Get the time limit. // hr = pPropList->Get(L"__PADS_SEARCHPREF_TIME_LIMIT", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidLongProperty(pPropVals)); _searchPrefs._dwTimeLimit = pPropVals->pPropArray[0].pUmiValue->uValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Get the attributes only preference. // hr = pPropList->Get(L"__PADS_SEARCHPREF_ATTRIBTYPES_ONLY", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidBoolProperty(pPropVals)); _searchPrefs._fAttrsOnly = pPropVals->pPropArray[0].pUmiValue->bValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Get the search scope again ??? // // __PADS_SEARCHPREF_SEARCH_SCOPE (duplicate for consistency cause it should be for all ideally). // hr = pPropList->Get(L"__PADS_SEARCHPREF_TIMEOUT", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidLongProperty(pPropVals)); _searchPrefs._dwTimeLimit = pPropVals->pPropArray[0].pUmiValue->uValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // The page size is next. // hr = pPropList->Get(L"__PADS_SEARCHPREF_PAGESIZE", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidLongProperty(pPropVals)); _searchPrefs._dwPageSize = pPropVals->pPropArray[0].pUmiValue->uValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Get the paged time limit. // hr = pPropList->Get(L"__PADS_SEARCHPREF_PAGED_TIME_LIMIT", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidLongProperty(pPropVals)); _searchPrefs._dwPagedTimeLimit = pPropVals->pPropArray[0].pUmiValue->uValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Chase referrals comes next. // hr = pPropList->Get(L"__PADS_SEARCHPREF_CHASE_REFERRALS", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidLongProperty(pPropVals)); _searchPrefs._dwChaseReferrals = pPropVals->pPropArray[0].pUmiValue->uValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Get the sort preferences ---> more complex do later. // // __PADS_SEARCHPREF_SORT_ON ---> potential candidate for all providers. // // // Cache results. // pPropList->Get(L"__PADS_SEARCHPREF_CACHE_RESULTS", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidBoolProperty(pPropVals)); _searchPrefs._fCacheResults = pPropVals->pPropArray[0].pUmiValue->bValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // DirSync control --- do later. // __PADS_SEARCHPREF_DIRSYNC // // // Tombstone preferences. // hr = pPropList->Get(L"__PADS_SEARCHPREF_TOMBSTONE", 0, &pPropVals); BAIL_ON_FAILURE(hr); BAIL_ON_FAILURE(hr = VerifyValidBoolProperty(pPropVals)); _searchPrefs._fTombStone = pPropVals->pPropArray[0].pUmiValue->bValue[0]; pPropList->FreeMemory(0, (void *) pPropVals); pPropVals = NULL; // // Need to get the list of attributes. // // hr = pPropList->Get(L"__PADS_SEARCHPREF_ATTRIBUTES", 0, &pPropVals); BAIL_ON_FAILURE(hr); // // Need to change to check for NULL then set count to -1. // pszAttrNames = pPropVals->pPropArray[0].pUmiValue->pszStrValue; dwAttrCount = pPropVals->pPropArray[0].uCount; // // Special case to ask for NULL with 0 to get all attributes. // if (!dwAttrCount && !pszAttrNames) { dwAttrCount = (DWORD) -1; } // // Now that we have all the preferences, we can set the search prefs // and also execute the search. // hr = ADsExecuteSearch( _searchPrefs, _pszADsPath, _pszLdapServer, _pszLdapDn, pszFilter, pszAttrNames, dwAttrCount, & _hSearchHandle ); BAIL_ON_FAILURE(hr); _fSearchExecuted = TRUE; error: if (pPropVals) { // // Guess we need to use the query to free this ! // _pQuery->FreeMemory(0, (void *)pPropVals); } if (pPropList) { pPropList->Release(); } if (pszLangBuf) { FreeADsStr(pszLangBuf); } if (pszQueryText) { FreeADsStr(pszQueryText); } if (pSortKey) { if (pSortKey->pszAttrType) { FreeADsStr(pSortKey->pszAttrType); } if (pSortKey->pszReserved) { FreeADsStr(pSortKey->pszReserved); } FreeADsMem(pSortKey); } if (FAILED(hr)) { // // Should we really be doing this ? // LdapInitializeSearchPreferences(&_searchPrefs, FALSE); } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::GetNextObject. // // Synopsis: This routine gets the next object in the result set. It calls // get next row to get the next row and creates the equivalent // cgenobj (need to add code to prepopulate the attributes). // // Arguments: None. // // Returns: S_OK, or any appropriate error code. // // Modifies: Internal search preferences and also state search handle. // //---------------------------------------------------------------------------- HRESULT CUmiSearchHelper::GetNextObject(IUnknown **pUnk) { HRESULT hr = S_OK; ADS_SEARCH_COLUMN adsColumn; BSTR ADsPath = NULL; LPWSTR pszParent = NULL; LPWSTR pszCN = NULL; PADSLDP ldapHandle = NULL; LDAPMessage *pldapMsg = NULL; // // Zero init the struct to make sure we can free in error path. // memset(&adsColumn, 0, sizeof(ADS_SEARCH_COLUMN)); ADsAssert(_fSearchExecuted); if (!_hSearchHandle) { BAIL_ON_FAILURE(hr = E_FAIL); } // // Get the next row. // hr = ADsGetNextRow( _hSearchHandle, *_pCreds ); if (hr == S_ADS_NOMORE_ROWS) { // // Hit end of enumeration. // *pUnk = NULL; RRETURN(S_FALSE); } else if (FAILED(hr)) { BAIL_ON_FAILURE(hr); } // // Fetch the path and use it to create the object. // hr = ADsGetColumn( _hSearchHandle, L"ADsPath", *_pCreds, _dwPort, &adsColumn ); BAIL_ON_FAILURE(hr); if (adsColumn.dwADsType != ADSTYPE_CASE_IGNORE_STRING || adsColumn.dwNumValues != 1) { // // Bad type or number of return values, this should never happen. // BAIL_ON_FAILURE(hr = E_FAIL); } // // Get the whole ldap message for the current row, that way we can // prepopulate the object. Since we are picking up the actual message, // we cannot rely on GetNextColumnName to work properly. // hr = ADsHelperGetCurrentRowMessage( _hSearchHandle, &ldapHandle, // need this when getting the actual attributes. &pldapMsg ); BAIL_ON_FAILURE(hr); // // // For creating the object, the parent path and the rdn is needed. // hr = BuildADsParentPath( adsColumn.pADsValues[0].CaseIgnoreString, &pszParent, &pszCN ); BAIL_ON_FAILURE(hr); // // Maybe we should just get all the values for this row and // send into a constructor for the cgenobj ??? // hr = CLDAPGenObject::CreateGenericObject( pszParent, pszCN, *_pCreds, ADS_OBJECT_BOUND, ldapHandle, pldapMsg, _pIID ? (*_pIID) : IID_IUmiObject, (void **) pUnk ); BAIL_ON_FAILURE(hr); error: if (pszParent) { FreeADsStr(pszParent); } if (pszCN) { FreeADsStr(pszCN); } // // Calling this can not harm us. // ADsFreeColumn(&adsColumn); if (FAILED(hr) && *pUnk) { (*pUnk)->Release(); } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CUmiSearchHelper::ProcessSQLQuery (helper function). // // Synopsis: This routine takes the SQL Query text, parses it using the // wmi query parser and converts the query to preferences on // the underlying queryObjects interface proplist. // // Arguments: pszQueryText - SQL language query. // // Returns: S_OK, or any appropriate error code. // // Modifies: Search preferences on the underlying query object. // //---------------------------------------------------------------------------- HRESULT CUmiSearchHelper::ProcessSQLQuery( LPCWSTR pszQueryText, LPWSTR * ppszFilter, PADS_SORTKEY *ppADsSortKey ) { HRESULT hr = S_OK; IWbemQuery *pQuery = NULL; ULONG ulFeatures[] = { WMIQ_LF1_BASIC_SELECT, WMIQ_LF2_CLASS_NAME_IN_QUERY, WMIQ_LF6_ORDER_BY, WMIQ_LF24_UMI_EXTENSIONS }; SWbemRpnEncodedQuery *pRpn = NULL; LPWSTR *pszAttribArray = NULL; DWORD dwAttribCount = 0; LPWSTR pszFromListFilter = NULL; LPWSTR pszFilter = NULL; IUmiPropList *pIntfPropList = NULL; UMI_PROPERTY umiPropAttribs = { UMI_TYPE_LPWSTR, 1, UMI_OPERATION_UPDATE, L"__PADS_SEARCHPREF_ATTRIBUTES", NULL }; // // Will likely need to set the attribute list. // UMI_PROPERTY pOneUmiProp[1]; pOneUmiProp[0] = umiPropAttribs; UMI_PROPERTY_VALUES propUmiVals[1]; propUmiVals[0].uCount = 1; propUmiVals[0].pPropArray = pOneUmiProp; ADsAssert(ppszFilter); *ppszFilter = NULL; // // Create the WBEM parser object. // hr = CoCreateInstance( CLSID_WbemQuery, NULL, CLSCTX_INPROC_SERVER, IID_IWbemQuery, (LPVOID *) &pQuery ); BAIL_ON_FAILURE(hr); // // Set the query into the parser and try and parse the query. // hr = pQuery->SetLanguageFeatures( 0, sizeof(ulFeatures)/sizeof(ULONG), ulFeatures ); BAIL_ON_FAILURE(hr); hr = pQuery->Parse(L"SQL", pszQueryText, 0); BAIL_ON_FAILURE(hr); hr = pQuery->GetAnalysis( WMIQ_ANALYSIS_RPN_SEQUENCE, 0, (LPVOID *) &pRpn ); BAIL_ON_FAILURE(hr); if (!pRpn->m_uSelectListSize) { // // Sanity check to make sure we are selecting something. // BAIL_ON_FAILURE(hr = E_FAIL); } hr = HelperGetAttributeList( &dwAttribCount, &pszAttribArray, pRpn->m_uSelectListSize, pRpn->m_ppSelectList ); BAIL_ON_FAILURE(hr); // // As of now the from list cannot have anything in other than *. // hr = HelperGetFromList( &pszFromListFilter, pRpn ); BAIL_ON_FAILURE(hr); hr = HelperGetFilterFromWhereClause( &pszFilter, pRpn->m_uWhereClauseSize, pRpn->m_ppRpnWhereClause ); BAIL_ON_FAILURE(hr); // // See if we have an order by list, in SQL we support only one // order by clause. // if (pRpn->m_uOrderByListSize) { hr = HelperGetSortKey(pRpn, ppADsSortKey); BAIL_ON_FAILURE(hr); } // // Need to set the attribute list if applicable. // if ((dwAttribCount != (DWORD) -1) && dwAttribCount) { // // Need to set the property on the interface proplist. // hr = _pQuery->GetInterfacePropList( 0, &pIntfPropList ); BAIL_ON_FAILURE(hr); pOneUmiProp[0].uCount = dwAttribCount; pOneUmiProp[0].pUmiValue = (PUMI_VALUE) (LPVOID) pszAttribArray; hr = pIntfPropList->Put( L"__PADS_SEARCHPREF_ATTRIBUTES", 0, propUmiVals ); BAIL_ON_FAILURE(hr); } // // Need to return the filter back. // *ppszFilter = pszFilter; error: if (pQuery) { pQuery->Release(); } if (pszFromListFilter) { FreeADsStr(pszFromListFilter); } if (pIntfPropList) { pIntfPropList->Release(); } // // We might need to return this as a param. // if (FAILED(hr) && pszFilter) { FreeADsStr(pszFilter); } RRETURN(hr); } //**************************************************************************** // CQueryStack Implementation * //**************************************************************************** //+--------------------------------------------------------------------------- // Function: CQueryStack::CQueryStack - Constructor. // // Synopsis: Initializes the underlying list to NULL and count to 0. // // Arguments: None. // // Returns: N/A. // // Modifies: All member variables. // //---------------------------------------------------------------------------- CQueryStack::CQueryStack(): _pStackList(NULL), _dwElementCount(0) { } //+--------------------------------------------------------------------------- // Function: CQueryStack::~CQueryStack - Destructor. // // Synopsis: Frees the underlying list contents if applicable. // // Arguments: None. // // Returns: N/A. // // Modifies: N/A. // //---------------------------------------------------------------------------- CQueryStack::~CQueryStack() { if (_pStackList) { STACKLIST *pList = _pStackList; STACKLIST *pPrev = NULL; // // Walk through and free the list. // while (pList) { pPrev = pList; pList = pList->pNext; // // Need to free prev node and its contents. // CQueryStack::FreeStackEntry(pPrev); } _pStackList = NULL; } } //+--------------------------------------------------------------------------- // Function: CQueryStack::Push. // // Synopsis: Pushes the node onto the stack and increases the count. The // node is added to the head of the underlying list. // // Arguments: pszString - The string value of the node. // dwType - The type of the node can either be // a literal or an operator. // // Returns: S_OK or E_OUTOFMEMORY. // // Modifies: The underlying list. // //---------------------------------------------------------------------------- HRESULT CQueryStack::Push( LPWSTR pszString, DWORD dwType ) { HRESULT hr = S_OK; PSTACKLIST pStackEntry = NULL; // // Allocate the new node. // hr = CQueryStack::AllocateStackEntry( pszString, dwType, &pStackEntry ); if (FAILED(hr)) { BAIL_ON_FAILURE(hr); } // // If the list is not already then it is added to the head. // if (!this->_pStackList) { _pStackList = pStackEntry; } else { // // Add new entry to the head of the list. // pStackEntry->pNext = _pStackList; _pStackList = pStackEntry; } // // Increment count as the add to list was successful. // _dwElementCount++; error: RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CQueryStack::Pop. // // Synopsis: Removes node node from the stack and returns the contents. // Node is removed from the head of the underlying list. // // Arguments: ppszString - Return ptr for string value of node. // dwType - Return ptr for the type of the node. // // Returns: S_OK, E_OUTOFMEMORY or E_FAIL. // // Modifies: The underlying list. // //---------------------------------------------------------------------------- HRESULT CQueryStack::Pop( LPWSTR *ppszString, DWORD *pdwType ) { HRESULT hr = S_OK; PSTACKLIST pStackEntry = _pStackList; if (!pStackEntry || !ppszString || !pdwType ) { BAIL_ON_FAILURE(hr = E_FAIL); } // // Initialize the return values. // *ppszString = NULL; *pdwType = (DWORD) -1; if (pStackEntry->pszElement) { *ppszString = AllocADsStr(pStackEntry->pszElement); if (!*ppszString) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } *pdwType = pStackEntry->dwElementType; } else { // // Should never get here. // BAIL_ON_FAILURE(hr = E_FAIL); } // // Need to advance the list to the next node and free the // contents of the current node. // _pStackList = _pStackList->pNext; CQueryStack::FreeStackEntry(pStackEntry); // // Decrement count as the remove from list was successful. // _dwElementCount--; error: RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CQueryStack::IsEmpty. // // Synopsis: Returns TRUE or FALSE based on the contents of the stack. // // Arguments: N/A. // // Returns: TRUE or FALSE. // // Modifies: N/A // //---------------------------------------------------------------------------- BOOL CQueryStack::IsEmpty() { return (_dwElementCount == 0); } //+--------------------------------------------------------------------------- // Function: CQueryStack::AllocateStackEntry - static helper routine. // // Synopsis: Allocates a stack node with the specified contents. // // Arguments: pszString - The string value of the node. // dwType - The type of the node can either be // a literal or an operator. // ppStackEntry - Return value. // // Returns: S_OK, E_OUTOFMEMORY or E_FAIL. // // Modifies: ppStackEntry. // //---------------------------------------------------------------------------- HRESULT CQueryStack::AllocateStackEntry( LPWSTR pszString, DWORD dwType, STACKLIST **ppStackEntry ) { HRESULT hr = S_OK; LPWSTR pszTmpString = NULL; if (!pszString || !dwType) { BAIL_ON_FAILURE(hr = E_FAIL); } *ppStackEntry = NULL; pszTmpString = AllocADsStr(pszString); if (!pszString) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } // // Allocate the new stack node. // *ppStackEntry = (PSTACKLIST) AllocADsMem(sizeof(STACKLIST)); if (!*ppStackEntry) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } (*ppStackEntry)->pszElement = pszTmpString; (*ppStackEntry)->dwElementType = dwType; error: if (FAILED(hr) && pszTmpString) { FreeADsStr(pszTmpString); } RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CQueryStack::FreeStackEntry - static helper routine. // // Synopsis: Frees contents of the stack node and the node itself. // // Arguments: pStackEntry - Pointer to stack node to free. // // Returns: N/A. // // Modifies: pStackEntry and contents. // //---------------------------------------------------------------------------- void CQueryStack::FreeStackEntry( PSTACKLIST pStackEntry ) { if (!pStackEntry) { return; } // // Free the string if there is one and then the node. // if (pStackEntry->pszElement) { FreeADsStr(pStackEntry->pszElement); } FreeADsMem(pStackEntry); return; }