//--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1996 // // File: cenumgrp.cxx // // Contents: LDAP GroupCollection Enumeration Code // // CLDAPGroupCollectionEnum:: // CLDAPGroupCollectionEnum:: // CLDAPGroupCollectionEnum:: // CLDAPGroupCollectionEnum:: // // History: //---------------------------------------------------------------------------- #include "ldap.hxx" #pragma hdrstop HRESULT BuildADsPathFromLDAPPath( LPWSTR szNamespace, LPWSTR szLdapDN, LPWSTR * ppszADsPathName ); //+--------------------------------------------------------------------------- // // Function: LDAPEnumVariant::Create // // Synopsis: // // Arguments: [pCollection] // [ppEnumVariant] // // Returns: HRESULT // // Modifies: // // History: 01-30-95 krishnag Created. // //---------------------------------------------------------------------------- HRESULT CLDAPGroupCollectionEnum::Create( CLDAPGroupCollectionEnum FAR* FAR* ppenumvariant, BSTR Parent, BSTR ADsPath, BSTR GroupName, VARIANT vMembers, VARIANT vFilter, CCredentials& Credentials, IDirectoryObject * pIDirObj, BOOL fRangeRetrieval ) { HRESULT hr = NOERROR; CLDAPGroupCollectionEnum FAR* penumvariant = NULL; long lLBound = 0; long lUBound = 0; *ppenumvariant = NULL; penumvariant = new CLDAPGroupCollectionEnum(); if (!penumvariant) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } penumvariant->_fRangeRetrieval = fRangeRetrieval; hr = ADsAllocString( Parent , &penumvariant->_Parent); BAIL_ON_FAILURE(hr); hr = ADsAllocString(GroupName, &penumvariant->_GroupName); BAIL_ON_FAILURE(hr); hr = ADsAllocString(ADsPath, &penumvariant->_ADsPath); BAIL_ON_FAILURE(hr); hr = VariantCopy( &penumvariant->_vMembers, &vMembers ); BAIL_ON_FAILURE(hr); if ( vMembers.vt == VT_BSTR ) // 1 member only { penumvariant->_lMembersCount = 1; } else { hr = SafeArrayGetLBound(V_ARRAY(&penumvariant->_vMembers), 1, (long FAR *)&lLBound ); BAIL_ON_FAILURE(hr); hr = SafeArrayGetUBound(V_ARRAY(&penumvariant->_vMembers), 1, (long FAR *)&lUBound ); BAIL_ON_FAILURE(hr); penumvariant->_lMembersCount = lUBound - lLBound + 1; } hr = ObjectTypeList::CreateObjectTypeList( vFilter, &penumvariant->_pObjList ); BAIL_ON_FAILURE(hr); penumvariant->_Credentials = Credentials; pIDirObj->QueryInterface( IID_IDirectoryObject, (void **) &(penumvariant->_pIDirObj) ); BAIL_ON_FAILURE(hr); *ppenumvariant = penumvariant; RRETURN(hr); error: delete penumvariant; RRETURN_EXP_IF_ERR(hr); } //---------------------------------------------------------------------------- // // Function: // // Synopsis: // //---------------------------------------------------------------------------- CLDAPGroupCollectionEnum::CLDAPGroupCollectionEnum() : _Parent(NULL), _ADsPath(NULL), _GroupName(NULL), _lCurrentIndex(0), _lMembersCount(0), _pIDirObj(NULL), _fRangeRetrieval(FALSE), _fAllRetrieved(FALSE), _pszRangeToFetch(NULL), _pAttributeEntries(NULL), _pCurrentEntry(NULL), _dwCurRangeIndex(0), _dwCurRangeMax(0), _dwNumEntries(0), _fLastSet(FALSE) { VariantInit( &_vMembers ); _pObjList = NULL; } //---------------------------------------------------------------------------- // // Function: // // Synopsis: // //---------------------------------------------------------------------------- CLDAPGroupCollectionEnum::CLDAPGroupCollectionEnum( ObjectTypeList ObjList ) : _Parent(NULL), _ADsPath(NULL), _GroupName(NULL), _lCurrentIndex(0), _lMembersCount(0) { VariantInit( &_vMembers ); _pObjList = NULL; } //---------------------------------------------------------------------------- // // Function: // // Synopsis: // //---------------------------------------------------------------------------- CLDAPGroupCollectionEnum::~CLDAPGroupCollectionEnum() { VariantClear( &_vMembers ); delete _pObjList; if ( _Parent ) ADsFreeString( _Parent ); if ( _GroupName ) ADsFreeString( _GroupName ); if ( _ADsPath ) ADsFreeString( _ADsPath ); if (_pIDirObj) { _pIDirObj->Release(); } if (_pszRangeToFetch) { FreeADsStr(_pszRangeToFetch); } if (_pAttributeEntries) { FreeADsMem(_pAttributeEntries); } } //---------------------------------------------------------------------------- // // Function: // // Synopsis: // //---------------------------------------------------------------------------- HRESULT CLDAPGroupCollectionEnum::EnumGroupMembers( ULONG cElements, VARIANT FAR* pvar, ULONG FAR* pcElementFetched ) { HRESULT hr = S_FALSE; IDispatch *pDispatch = NULL; DWORD i = 0; IADs * pIADs = NULL; BSTR pszClass = NULL; DWORD dwClassID; BSTR pszFilterName = NULL; BOOL fFound = FALSE; BOOL fEmpty = TRUE; while (i < cElements) { hr = GetUserMemberObject(&pDispatch); if (FAILED(hr)) { // // Set hr to S_FALSE as all our enumerators are not // built to handle a failure hr but only S_OK and S_FALSE. // hr = S_FALSE; } if (hr == S_FALSE) { break; } // // Apply the IADsMembers::put_Filter filter. // If the enumerated object is not one of the types to be returned, // go on to the next member of the group. // hr = pDispatch->QueryInterface(IID_IADs, (void **)&pIADs); BAIL_ON_FAILURE(hr); // // To check whether use specifies filter // fEmpty = _pObjList->IsEmpty(); // // User specifies the filter // if (!fEmpty) { // // Determine the object class of the enumerated object and the corresponding // object class ID number (as specified in the Filters global array). // hr = pIADs->get_Class(&pszClass); BAIL_ON_FAILURE(hr); // // Enumerate through the object classes listed in the user-specified filter // until we either find a match (fFound = TRUE) or we reach the end of the // list. // hr = _pObjList->Reset(); // // compare with the user defined filter // while (SUCCEEDED(hr)) { hr = _pObjList->GetCurrentObject(&pszFilterName); if (SUCCEEDED(hr) && (!_wcsicmp(pszClass, pszFilterName)) ) { fFound = TRUE; if(pszFilterName) { SysFreeString(pszFilterName); pszFilterName = NULL; } break; } if(pszFilterName) { SysFreeString(pszFilterName); pszFilterName = NULL; } hr = _pObjList->Next(); } if (!fFound) { // // not on the list of objects to return, try again // with the next member of the group // pDispatch->Release(); pIADs->Release(); if (pszClass) { ADsFreeString(pszClass); pszClass = NULL; } continue; } pIADs->Release(); if (pszClass) { ADsFreeString(pszClass); pszClass = NULL; } } // // Return it. // VariantInit(&pvar[i]); pvar[i].vt = VT_DISPATCH; pvar[i].pdispVal = pDispatch; (*pcElementFetched)++; i++; } RRETURN_EXP_IF_ERR(hr); error: if (pDispatch) { pDispatch->Release(); } if (pIADs) { pIADs->Release(); } if (pszClass) { ADsFreeString(pszClass); } RRETURN_EXP_IF_ERR(hr); } //---------------------------------------------------------------------------- // // Function: // // Synopsis: // //---------------------------------------------------------------------------- HRESULT CLDAPGroupCollectionEnum::GetUserMemberObject( IDispatch ** ppDispatch ) { HRESULT hr = S_OK; VARIANT v; IUnknown *pObject = NULL; TCHAR *pszADsPath = NULL; LPWSTR pszUserName = NULL; LPWSTR pszPassword = NULL; DWORD dwAuthFlags = 0; BOOL fRangeUsed = FALSE; hr = _Credentials.GetUserName(&pszUserName); BAIL_ON_FAILURE(hr); hr = _Credentials.GetPassword(&pszPassword); BAIL_ON_FAILURE(hr); dwAuthFlags = _Credentials.GetAuthFlags(); while ( TRUE ) { VariantInit(&v); if ( _lCurrentIndex >= _lMembersCount ) { hr = S_FALSE; // // See if we need to fetch members using Rangeretrieval. // if (_fRangeRetrieval && !_fAllRetrieved) { hr = GetNextMemberRange(&v); BAIL_ON_FAILURE(hr); if (hr == S_FALSE) { goto error; } fRangeUsed = TRUE; } else goto error; } // // Variant v will have correct value already if range // retrieval was used. // if (!fRangeUsed) { if ( _vMembers.vt == VT_BSTR ) { hr = VariantCopy( &v, &_vMembers ); } else { hr = SafeArrayGetElement( V_ARRAY(&_vMembers), &_lCurrentIndex, &v); } BAIL_ON_FAILURE(hr); } _lCurrentIndex++; LPTSTR pszMember = V_BSTR(&v); LPTSTR pszTemp = NULL; hr = BuildADsPathFromLDAPPath( _Parent, pszMember, &pszADsPath ); BAIL_ON_FAILURE(hr); hr = ADsOpenObject( pszADsPath, pszUserName, pszPassword, dwAuthFlags, IID_IUnknown, (LPVOID *)&pObject ); if ( pszADsPath ) { FreeADsStr( pszADsPath ); pszADsPath = NULL; } if (pszPassword) { FreeADsStr(pszPassword); pszPassword = NULL; } if (pszUserName) { FreeADsStr(pszUserName); pszUserName = NULL; } VariantClear(&v); // // If we failed to get the current object, continue with the next one // if ( FAILED(hr)) continue; hr = pObject->QueryInterface( IID_IDispatch, (LPVOID *) ppDispatch ); BAIL_ON_FAILURE(hr); pObject->Release(); RRETURN(S_OK); } error: if ( pObject ) pObject->Release(); if ( pszADsPath ) FreeADsStr( pszADsPath ); if (pszPassword) { FreeADsStr(pszPassword); } if (pszUserName) { FreeADsStr(pszUserName); } VariantClear(&v); *ppDispatch = NULL; RRETURN(hr); } //+--------------------------------------------------------------------------- // // Function: CLDAPGroupCollectionEnum::Next // // Synopsis: Returns cElements number of requested NetOle objects in the // array supplied in pvar. // // Arguments: [cElements] -- The number of elements requested by client // [pvar] -- ptr to array of VARIANTs to for return objects // [pcElementFetched] -- if non-NULL, then number of elements // -- actually returned is placed here // // Returns: HRESULT -- S_OK if number of elements requested are returned // -- S_FALSE if number of elements is < requested // // Modifies: // // History: 11-3-95 krishnag Created. // //---------------------------------------------------------------------------- STDMETHODIMP CLDAPGroupCollectionEnum::Next( ULONG cElements, VARIANT FAR* pvar, ULONG FAR* pcElementFetched ) { ULONG cElementFetched = 0; HRESULT hr = S_OK; hr = EnumGroupMembers( cElements, pvar, &cElementFetched ); if (pcElementFetched) { *pcElementFetched = cElementFetched; } RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPGroupCollectionEnum::UpdateRangeToFetch() { HRESULT hr = S_OK; WCHAR szPath[512]; // // szPath should be big enough to handle any range we // can reasonably expect and will be used to build the // member string. // if (_pszRangeToFetch == NULL) { // // Rather than ask for the first n elements again, // we can use the count we have in the variant array // to decide where we need to start. // wsprintf(szPath, L"member;range=%d-*", _lMembersCount); _pszRangeToFetch = AllocADsStr(szPath); if (!_pszRangeToFetch) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } } else { // // In this case the call to GetObjectAttr has been made // and we need to get the info out of the name. // BOOL fUpdated = FALSE; for (DWORD i = 0; (i < _dwNumEntries) && !fUpdated; i++) { LPWSTR pszTemp = NULL; LPWSTR pszAttrName = _pAttributeEntries[i].pszAttrName; LPWSTR pszStar = NULL; if (wcslen(pszAttrName) > wcslen(L"member;range=")) { // // See if we have our string // if (!_wcsnicmp( pszAttrName, L"member;range=", wcslen(L"member;range") ) ) { _pCurrentEntry = &(_pAttributeEntries[i]); _dwCurRangeMax = _pCurrentEntry->dwNumValues; _dwCurRangeIndex = 0; pszTemp = wcschr(pszAttrName, L'='); if (!pszTemp) { // // No chance of recovery from this. // BAIL_ON_FAILURE(hr = E_FAIL); } // // Move the lower part of range. // *pszTemp++; if (!*pszTemp) { BAIL_ON_FAILURE(hr = E_FAIL); } pszStar = wcschr(pszTemp, L'*'); if (pszStar) { // // Do not bother with any udpate of the range, // we have all the entries. // _fLastSet = TRUE; goto error; } DWORD dwLower = 0; DWORD dwHigher = 0; if (!swscanf(pszTemp, L"%d-%d", &dwLower, &dwHigher)) { BAIL_ON_FAILURE(hr = E_FAIL); } dwHigher++; wsprintf(szPath, L"member;range=%d-*", dwHigher); FreeADsStr(_pszRangeToFetch); _pszRangeToFetch = AllocADsStr(szPath); if (!_pszRangeToFetch) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } // // Set flag so we can get out of the loop. // fUpdated = TRUE; } // this was not member; } // is the length greater than that of member; } // for each entry in attribute entries. if (!fUpdated) { // // Failed cause there was no members or a range. // BAIL_ON_FAILURE(hr = E_FAIL); } } // _pszRangeToFetch was non NULL error: RRETURN(hr); } //+--------------------------------------------------------------------------- // // Function: CLDAPGroupCollectionEnum::GetNextMemberRange // // Synopsis: Returns a variant bstr with the dn of the next member in // the group or FALSE if there are no more. This routine will // will use IDirectoryObject to fetch more members if applicable. // // // Arguments: [pVarMemberBstr] -- ptr to VARIANT for return bstr. // // Returns: HRESULT -- S_OK if number of elements requested are returned // -- S_FALSE if number of elements is < requested // -- Other failure hr's. // Modifies: // // History: 9-12-99 AjayR Created. // //---------------------------------------------------------------------------- HRESULT CLDAPGroupCollectionEnum::GetNextMemberRange( VARIANT FAR* pVarMemberBstr ) { HRESULT hr = S_FALSE; if (_fAllRetrieved) { RRETURN(S_FALSE); } // // Initialize the range to fetch if applicable. // if (_pszRangeToFetch == NULL) { hr = UpdateRangeToFetch(); BAIL_ON_FAILURE(hr); } if (_dwCurRangeIndex == _dwCurRangeMax) { // // Call into wrapper for GetObjectAttributes. // if (_fLastSet) { _fAllRetrieved = TRUE; hr = S_FALSE; } else { hr = UpdateAttributeEntries(); BAIL_ON_FAILURE(hr); } if (hr == S_FALSE) { goto error; } } // // At this point we should have the entries in our current // return set. // if (!_pCurrentEntry) { BAIL_ON_FAILURE(hr = E_FAIL); } if (_dwCurRangeIndex < _dwCurRangeMax) { hr = ADsAllocString( _pCurrentEntry->pADsValues[_dwCurRangeIndex].DNString, &V_BSTR(pVarMemberBstr) ); BAIL_ON_FAILURE(hr); } _dwCurRangeIndex++; error: RRETURN(hr); } HRESULT CLDAPGroupCollectionEnum::UpdateAttributeEntries() { HRESULT hr = S_OK; LPWSTR aStrings[2]; ADsAssert(_pszRangeToFetch || !"Range is NULL internal error"); if (_pAttributeEntries) { FreeADsMem(_pAttributeEntries); _pAttributeEntries = NULL; } aStrings[0] = _pszRangeToFetch; aStrings[1] = NULL; hr = _pIDirObj->GetObjectAttributes( aStrings, 1, &_pAttributeEntries, &_dwNumEntries ); BAIL_ON_FAILURE(hr); if (_dwNumEntries == 0) { hr = S_FALSE; } else { // // Will return error if member was not there. // hr = UpdateRangeToFetch(); BAIL_ON_FAILURE(hr); } error: RRETURN(hr); }