//--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1995 // // File: cenumdom.cxx // // Contents: LDAP Object Enumeration Code // // CLDAPGenObjectEnum::CLDAPGenObjectEnum() // CLDAPGenObjectEnum::CLDAPGenObjectEnum // // History: //---------------------------------------------------------------------------- #include "ldap.hxx" #pragma hdrstop HRESULT BuildLDAPFilterArray( VARIANT vFilter, LPTSTR *ppszFilter ); #define FILTER_BUFFER_LEN 256 //+--------------------------------------------------------------------------- // // Function: CLDAPEnumVariant::Create // // Synopsis: // // Arguments: [pCollection] // [ppEnumVariant] // // Returns: HRESULT // // Modifies: // // History: 01-30-95 krishnag Created. // //---------------------------------------------------------------------------- HRESULT CLDAPGenObjectEnum::Create( CLDAPGenObjectEnum FAR* FAR* ppenumvariant, BSTR ADsPath, PADSLDP pLdapHandle, IADs *pADs, VARIANT vFilter, CCredentials& Credentials, DWORD dwOptReferral, DWORD dwPageSize ) { HRESULT hr = NOERROR; CLDAPGenObjectEnum FAR* penumvariant = NULL; TCHAR *pszLDAPServer = NULL; DWORD dwModificationTime = 0L; DWORD dwNumberOfEntries = 0L; LPTSTR aStrings[2]; DWORD dwPort = 0; WCHAR **aValues = NULL; int nCount = 0; DWORD totalCount = 0; DWORD dwcEntriesReturned = 0; PLDAPControl pPagedControl = NULL; LDAPControl referralControl = { LDAP_CONTROL_REFERRALS_W, { sizeof( DWORD ), (PCHAR) &dwOptReferral }, TRUE }; PLDAPControl clientControls[2] = { &referralControl, NULL }; PLDAPControl serverControls[2] = {NULL, NULL}; PLDAPControl *serverReturnedControls = NULL; penumvariant = new CLDAPGenObjectEnum(); if (!penumvariant) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } hr = ADsAllocString( ADsPath, &penumvariant->_ADsPath); BAIL_ON_FAILURE(hr); penumvariant->_dwOptReferral = dwOptReferral; penumvariant->_dwPageSize = dwPageSize; hr = BuildLDAPFilterArray( vFilter, &penumvariant->_pszFilter ); if ( FAILED(hr) || !penumvariant->_pszFilter ) // this might happen when vFilter is empty { hr = S_OK; penumvariant->_pszFilter = AllocADsStr(TEXT("(objectClass=*)")); if ( penumvariant->_pszFilter == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } } penumvariant->_Credentials = Credentials; hr = BuildLDAPPathFromADsPath2( ADsPath, &pszLDAPServer, &penumvariant->_pszLDAPDn, &dwPort ); BAIL_ON_FAILURE(hr); penumvariant->_pLdapHandle = pLdapHandle; // Need to AddRef since we are storing the ld that is owned by the object pADs->AddRef(); penumvariant->_pADs = pADs; aStrings[0] = TEXT("objectClass"); aStrings[1] = NULL; // // Check if paged search control is supported; if so, we will use it. // hr = ReadPagingSupportedAttr( pszLDAPServer, &penumvariant->_fPagedSearch, Credentials, dwPort ) ; if (penumvariant->_fPagedSearch) { // // In this case we wont to use the ldap paging API // wrapper rather than use cookies directly. // hr = LdapSearchInitPage( penumvariant->_pLdapHandle, penumvariant->_pszLDAPDn, LDAP_SCOPE_ONELEVEL, penumvariant->_pszFilter, aStrings, 0, NULL, clientControls, 0, 0, NULL, &penumvariant->_phPagedSearch ); if (FAILED(hr) || (penumvariant->_phPagedSearch == NULL)) { // // Some error in setting up the paged control, default to non // paged search. // penumvariant->_fPagedSearch = FALSE; } } if (!penumvariant->_fPagedSearch) { hr = LdapSearchExtS( penumvariant->_pLdapHandle, penumvariant->_pszLDAPDn, LDAP_SCOPE_ONELEVEL, penumvariant->_pszFilter, aStrings, 0, NULL, clientControls, NULL, 0, &penumvariant->_res ); // // Error out only if there are no entries returned. // dwcEntriesReturned = LdapCountEntries( penumvariant->_pLdapHandle, penumvariant->_res ); if (FAILED(hr) && !dwcEntriesReturned) { BAIL_ON_FAILURE(hr); } } // if not do the paged search case else { if (penumvariant->_fPagedSearch) { // // Get the first page full of results. // hr = LdapGetNextPageS( penumvariant->_pLdapHandle, penumvariant->_phPagedSearch, NULL, penumvariant->_dwPageSize, &totalCount, &penumvariant->_res ); if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) { penumvariant->_fLastPage = TRUE; goto error; } else if ((hr == HRESULT_FROM_WIN32(ERROR_DS_SIZELIMIT_EXCEEDED)) && totalCount != 0) { penumvariant->_fLastPage = TRUE; hr = S_OK; } BAIL_ON_FAILURE(hr); } } // the paged search case error: if ( pszLDAPServer ) FreeADsStr( pszLDAPServer); if (pPagedControl) { LdapControlFree(pPagedControl); } if (serverReturnedControls) { LdapControlsFree(serverReturnedControls); } if (FAILED(hr) && !dwcEntriesReturned) { if (penumvariant) delete penumvariant; *ppenumvariant = NULL; } else { // // entries successfully retrieved from server, return them // to client even if error and let client decide // *ppenumvariant = penumvariant; } RRETURN_EXP_IF_ERR(hr); } CLDAPGenObjectEnum::CLDAPGenObjectEnum(): _ADsPath(NULL), _fAllEntriesReturned(FALSE), _fPagedSearch(FALSE), _fLastPage(FALSE), _dwOptReferral(LDAP_CHASE_EXTERNAL_REFERRALS), _pszFilter(NULL), _pszLDAPDn(NULL), _phPagedSearch(NULL) { _pObjList = NULL; _pLdapHandle = NULL; _pADs = NULL; _res = NULL; _entry = NULL; } CLDAPGenObjectEnum::~CLDAPGenObjectEnum() { if ( _res ) LdapMsgFree( _res ); if (_pszFilter) { FreeADsMem(_pszFilter); } if (_pszLDAPDn) { FreeADsMem(_pszLDAPDn); } if (_fPagedSearch) { LdapSearchAbandonPage(_pLdapHandle, _phPagedSearch); } if ( _pADs ) _pADs->Release(); if ( _ADsPath ) ADsFreeString( _ADsPath ); if ( _pObjList ) delete _pObjList; _pLdapHandle = NULL; _phPagedSearch = NULL; } HRESULT CLDAPGenObjectEnum::EnumGenericObjects( ULONG cElements, VARIANT FAR* pvar, ULONG FAR* pcElementFetched ) { HRESULT hr = S_OK; IDispatch *pDispatch = NULL; DWORD i = 0; BOOL fRepeat = FALSE; DWORD dwFailureCount = 0; DWORD dwPermitFailure = 1000; while (i < cElements) { hr = GetGenObject(&pDispatch); if (hr == S_FALSE) { break; } else if (FAILED(hr)) { // // Got an error while retrieving the object, ignore the // error and continue with the next object. // If continuously getting error more than dwPermitFailure, // make the return value S_FALSE, leave the loop. // if (fRepeat) { dwFailureCount++; if(dwFailureCount > dwPermitFailure) { hr = S_FALSE; break; } } else { fRepeat = TRUE; dwFailureCount = 1; } hr = S_OK; continue; } if (fRepeat) { fRepeat = FALSE; } VariantInit(&pvar[i]); pvar[i].vt = VT_DISPATCH; pvar[i].pdispVal = pDispatch; (*pcElementFetched)++; i++; } return(hr); } HRESULT CLDAPGenObjectEnum::GetGenObject( IDispatch ** ppDispatch ) { HRESULT hr = S_OK; LPTSTR pszObjectName = NULL; TCHAR **aValues = NULL; int nCount = 0; DWORD totalCount = 0; TCHAR szADsClassName[64]; LPWSTR aStrings[2] = {TEXT("objectClass"), NULL}; DWORD dwPort; LPWSTR pszLDAPServer = NULL; LPWSTR pszLDAPDn = NULL; LPWSTR pszADsPathParent = NULL; BOOL fGCNameSpace = FALSE; PLDAPControl pPagedControl = NULL; PLDAPControl serverControls[2] = {NULL, NULL}; PLDAPControl *serverReturnedControls = NULL; LDAPControl referralControl = { LDAP_CONTROL_REFERRALS_W, { sizeof( DWORD ), (PCHAR) &_dwOptReferral }, TRUE }; PLDAPControl clientControls[2] = { &referralControl, NULL }; OBJECTINFO ObjectInfoLocal; memset(&ObjectInfoLocal, 0, sizeof(OBJECTINFO)); *ppDispatch = NULL; if ( _fAllEntriesReturned ) { hr = S_FALSE; goto error; } if ( _entry == NULL ) hr = LdapFirstEntry( _pLdapHandle, _res, &_entry ); else hr = LdapNextEntry( _pLdapHandle, _entry, &_entry ); if (FAILED(hr) ) { _fAllEntriesReturned = TRUE; BAIL_ON_FAILURE(hr); } else if ( _entry == NULL ) { if (!_fPagedSearch || _fLastPage) { // reached the end of enumeration hr = S_FALSE; _fAllEntriesReturned = TRUE; goto error; } else { // more pages are remaining // // release this page // if (_res) { LdapMsgFree( _res ); } // // Get the next page of results // // // Get the first page full of results. // hr = LdapGetNextPageS( _pLdapHandle, _phPagedSearch, NULL, _dwPageSize, &totalCount, &_res ); if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) { _fLastPage = TRUE; hr = S_FALSE; goto error; } BAIL_ON_FAILURE(hr); hr = LdapFirstEntry(_pLdapHandle, _res, &_entry ); if (FAILED(hr) ) { _fAllEntriesReturned = TRUE; BAIL_ON_FAILURE(hr); } else if ( (_entry == NULL) ) { // reached the end of enumeration hr = S_FALSE; _fAllEntriesReturned = TRUE; goto error; } } } hr = LdapGetDn( _pLdapHandle, _entry, &pszObjectName ); if (FAILED(hr) ) { BAIL_ON_FAILURE(hr); } hr = LdapGetValues( _pLdapHandle, _entry, TEXT("objectClass"), &aValues, &nCount ); BAIL_ON_FAILURE(hr); if ( nCount == 0 ) { // This object exists but does not contain objectClass attribute // which is required for all DS objects. Hence, ignore the object // and return error. hr = E_ADS_PROPERTY_NOT_FOUND; BAIL_ON_FAILURE(hr); } // // Now send back the current object // { CLexer Lexer(pszObjectName); hr = InitObjectInfo(pszObjectName, &ObjectInfoLocal); BAIL_ON_FAILURE(hr); Lexer.SetAtDisabler(TRUE); Lexer.SetFSlashDisabler(TRUE); hr = PathName(&Lexer, &ObjectInfoLocal); BAIL_ON_FAILURE(hr); Lexer.SetFSlashDisabler(FALSE); if (ObjectInfoLocal.ComponentArray[0].szValue == NULL) { BAIL_ON_FAILURE(hr=E_ADS_BAD_PATHNAME); } DWORD len; len = wcslen(ObjectInfoLocal.ComponentArray[0].szComponent) + wcslen(ObjectInfoLocal.ComponentArray[0].szValue) + 1; // For equal to sign pszObjectName[len] = '\0'; // // We form the ADsPath to the parent by taking the _ADsPath of this // object (which is the parent ADsPath), chopping off the DN portion, // and attaching the parent DN portion of the object retrieved. This // is so that the enumerated child objects get a proper parent ADsPath // even if the parent object was bound using a GUID. // hr = BuildLDAPPathFromADsPath2( _ADsPath, &pszLDAPServer, &pszLDAPDn, &dwPort ); BAIL_ON_FAILURE(hr); // _ADsPath could not be NULL if(!_wcsnicmp(L"GC:", _ADsPath, wcslen(L"GC:"))) { fGCNameSpace = TRUE; } if (ObjectInfoLocal.NumComponents > 1) { // // pszObjectName[0 ... len] = object name (e.g., "CN=child") // pszObjectName[len+1 ....] = parent name (e.g., "OU=parent, DC=....") // hr = BuildADsPathFromLDAPPath2( (pszLDAPServer ? TRUE : FALSE), fGCNameSpace ? L"GC:" : L"LDAP:", pszLDAPServer, dwPort, &(pszObjectName[len+1]), &pszADsPathParent ); } else { // // If the count is zero or less, then the objectName will // need to be created with a parent path of NULL. In this case // pszObjectName[len+1] is not necessarily valid. This is // espescially true when we have a defaultNamingContext // that is NULL or no defaultNamingContext with an object // directly underneath it (say dn is just o=testObject. // hr = BuildADsPathFromLDAPPath2( (pszLDAPServer ? TRUE : FALSE), fGCNameSpace ? L"GC:" : L"LDAP:", pszLDAPServer, dwPort, NULL, &pszADsPathParent ); } BAIL_ON_FAILURE(hr); } hr = CLDAPGenObject::CreateGenericObject( pszADsPathParent, pszObjectName, aValues, nCount, _Credentials, ADS_OBJECT_BOUND, IID_IDispatch, (void **) ppDispatch ); BAIL_ON_FAILURE(hr); error: FreeObjectInfo(&ObjectInfoLocal); if ( pszObjectName ) LdapMemFree( pszObjectName ); if ( aValues ) LdapValueFree( aValues ); if (pPagedControl) { LdapControlFree(pPagedControl); } if (serverReturnedControls) { LdapControlsFree(serverReturnedControls); } if (pszADsPathParent) { FreeADsMem(pszADsPathParent); } if (pszLDAPServer) { FreeADsMem(pszLDAPServer); } if (pszLDAPDn) { FreeADsMem(pszLDAPDn); } RRETURN_EXP_IF_ERR(hr); } //+--------------------------------------------------------------------------- // // Function: CLDAPGenObjectEnum::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 CLDAPGenObjectEnum::Next( ULONG cElements, VARIANT FAR* pvar, ULONG FAR* pcElementFetched ) { ULONG cElementFetched = 0; HRESULT hr = S_OK; hr = EnumGenericObjects( cElements, pvar, &cElementFetched ); if (pcElementFetched) { *pcElementFetched = cElementFetched; } RRETURN_EXP_IF_ERR(hr); } HRESULT BuildLDAPFilterArray( VARIANT var, LPTSTR *ppszFilter ) { HRESULT hr = S_OK; LONG dwSLBound = 0; LONG dwSUBound = 0; VARIANT v; LONG i; LONG dwBracketCount = 0; DWORD dwFilterBufLen = 0; DWORD dwCurrentFilterLen = 0; LPTSTR pszFilter = NULL, pszTempFilter = NULL; *ppszFilter = NULL; if(!(V_VT(&var) == (VT_VARIANT|VT_ARRAY))) { RRETURN(E_FAIL); } // // Check that there is only one dimension in this array // if ((V_ARRAY(&var))->cDims != 1) { hr = E_FAIL; BAIL_ON_FAILURE(hr); } // // Check that there is at least one element in this array // if ((V_ARRAY(&var))->rgsabound[0].cElements == 0){ hr = E_FAIL; BAIL_ON_FAILURE(hr); } // // We know that this is a valid single dimension array // hr = SafeArrayGetLBound(V_ARRAY(&var), 1, (long FAR *)&dwSLBound ); BAIL_ON_FAILURE(hr); hr = SafeArrayGetUBound(V_ARRAY(&var), 1, (long FAR *)&dwSUBound ); BAIL_ON_FAILURE(hr); dwCurrentFilterLen = 0; dwFilterBufLen = FILTER_BUFFER_LEN; pszFilter = (LPTSTR) AllocADsMem( (dwFilterBufLen + 1) * sizeof(WCHAR)); if ( pszFilter == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } for (i = dwSLBound; i <= dwSUBound; i++) { VariantInit(&v); hr = SafeArrayGetElement(V_ARRAY(&var), (long FAR *)&i, &v ); BAIL_ON_FAILURE(hr); // The last length below is the string length of // "(| (objectClass=))" which is 18. // while ( dwCurrentFilterLen + _tcslen(V_BSTR(&v)) + 18 > dwFilterBufLen) { pszTempFilter = (LPTSTR) ReallocADsMem( pszFilter, (dwFilterBufLen + 1) * sizeof(WCHAR), (dwFilterBufLen*2 + 1) * sizeof(WCHAR)); if ( pszTempFilter == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } pszFilter = pszTempFilter; dwFilterBufLen *= 2; } if ( i == dwSUBound ) { _tcscat( pszFilter, TEXT("(objectClass=")); } else { dwBracketCount++; _tcscat( pszFilter, TEXT("(| (objectClass=")); } _tcscat( pszFilter, V_BSTR(&v)); _tcscat( pszFilter, TEXT(")")); dwCurrentFilterLen = _tcslen(pszFilter); VariantClear(&v); } for ( i = 0; i < dwBracketCount; i++ ) _tcscat( pszFilter, TEXT(")")); *ppszFilter = pszFilter; RRETURN(S_OK); error: VariantClear(&v); if ( pszFilter != NULL) FreeADsMem( pszFilter ); RRETURN(hr); }