windows-nt/Source/XPSP1/NT/ds/adsi/ldap/cenumobj.cxx
2020-09-26 16:20:57 +08:00

849 lines
21 KiB
C++

//---------------------------------------------------------------------------
//
// 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);
}