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

4411 lines
131 KiB
C++

//---------------------------------------------------------------------------;
//
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995
//
// File: cdssrch.cxx
//
// Contents: Microsoft ADs LDAP Provider Generic Object
//
//
// History: 03-02-97 ShankSh Created.
//
//----------------------------------------------------------------------------
#include "ldapc.hxx"
#pragma hdrstop
const int MAX_BYTES = 1048576;
const int NO_LDAP_RESULT_HANDLES = 32;
static BOOL
IsValidPrefValue(
ADS_SEARCHPREF_INFO SearchPref
);
static
HRESULT
LdapValueToADsColumn(
LPWSTR pszColumnName,
DWORD dwSyntaxId,
DWORD dwValues,
VOID **lpValue,
ADS_SEARCH_COLUMN * pColumn
);
HRESULT
UnicodeToUTF8String(
LPCWSTR pUnicode,
LPSTR *ppUTF8
);
HRESULT
UTF8ToUnicodeString(
LPCSTR pUTF8,
LPWSTR *ppUnicode
);
//
// Sets the appropriate search preferences.
//
HRESULT
ADsSetSearchPreference(
IN PADS_SEARCHPREF_INFO pSearchPrefs,
IN DWORD dwNumPrefs,
OUT LDAP_SEARCH_PREF * pLdapPref,
IN LPWSTR pszLDAPServer,
IN LPWSTR pszLDAPDn,
IN CCredentials& Credentials,
IN DWORD dwPort
)
{
HRESULT hr = S_OK;
BOOL fWarning = FALSE;
DWORD i, j;
BOOL fPagedSearch = FALSE, fSorting = TRUE, fVLV = FALSE, fAttribScoped = FALSE;
DWORD dwSecDescType = ADSI_LDAPC_SECDESC_NONE;
PADS_SORTKEY pSortKeyArray = NULL;
DWORD dwSortKeyArrayLen = 0, nKeys = 0;
BOOL fSetCaching = FALSE; // TRUE if user explicitly set a caching preference
BOOL fSetScope = FALSE; // TRUE if user explicitly set a scope preference
PADS_VLV pVLV = NULL;
DWORD dwVLVLen = 0;
LPSTR pUTF8Target = NULL;
DWORD dwUTF8TargetLen = 0;
LPWSTR pAttribScoped = NULL;
DWORD dwSecurityMask;
if (!pSearchPrefs && dwNumPrefs > 0 || !pLdapPref) {
RRETURN (E_ADS_BAD_PARAMETER);
}
for (i=0; i<dwNumPrefs; i++) {
pSearchPrefs[i].dwStatus = ADS_STATUS_S_OK;
if (!IsValidPrefValue(pSearchPrefs[i]) ) {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
fWarning = TRUE;
continue;
}
switch(pSearchPrefs[i].dwSearchPref) {
case ADS_SEARCHPREF_ASYNCHRONOUS:
pLdapPref->_fAsynchronous = pSearchPrefs[i].vValue.Boolean;
break;
case ADS_SEARCHPREF_ATTRIBTYPES_ONLY:
pLdapPref->_fAttrsOnly = pSearchPrefs[i].vValue.Boolean;
break;
case ADS_SEARCHPREF_SIZE_LIMIT:
pLdapPref->_dwSizeLimit = pSearchPrefs[i].vValue.Integer;
break;
case ADS_SEARCHPREF_TIME_LIMIT:
pLdapPref->_dwTimeLimit = pSearchPrefs[i].vValue.Integer;
break;
case ADS_SEARCHPREF_TIMEOUT:
pLdapPref->_timeout.tv_sec = pSearchPrefs[i].vValue.Integer;
break;
case ADS_SEARCHPREF_PAGESIZE:
if (pLdapPref->_fDirSync) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
ReadPagingSupportedAttr(
pszLDAPServer,
&fPagedSearch,
Credentials,
dwPort
) ;
if (fPagedSearch) {
pLdapPref->_dwPageSize = pSearchPrefs[i].vValue.Integer;
}
else {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
fWarning = TRUE;
}
break;
case ADS_SEARCHPREF_PAGED_TIME_LIMIT:
ReadPagingSupportedAttr(
pszLDAPServer,
&fPagedSearch,
Credentials,
dwPort
) ;
if (fPagedSearch) {
pLdapPref->_dwPagedTimeLimit = pSearchPrefs[i].vValue.Integer;
}
else {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
fWarning = TRUE;
}
break;
case ADS_SEARCHPREF_DEREF_ALIASES:
pLdapPref->_dwDerefAliases = pSearchPrefs[i].vValue.Integer;
break;
case ADS_SEARCHPREF_SEARCH_SCOPE:
// if doing a attribute-scoped query and user tries to set scope
// to anything other than ADS_SCOPE_BASE, reject
if (pLdapPref->_pAttribScoped && (pSearchPrefs[i].vValue.Integer != ADS_SCOPE_BASE)) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
switch (pSearchPrefs[i].vValue.Integer) {
case ADS_SCOPE_SUBTREE:
pLdapPref->_dwSearchScope = LDAP_SCOPE_SUBTREE;
break;
case ADS_SCOPE_ONELEVEL:
pLdapPref->_dwSearchScope = LDAP_SCOPE_ONELEVEL;
break;
case ADS_SCOPE_BASE:
pLdapPref->_dwSearchScope = LDAP_SCOPE_BASE;
break;
}
fSetScope = TRUE; // set so if user later tries to do
// attrib-scoped query, and user set scope
// to other than ADS_SCOPE_BASE, can detect & reject
break;
case ADS_SEARCHPREF_CHASE_REFERRALS:
switch (pSearchPrefs[i].vValue.Integer) {
case ADS_CHASE_REFERRALS_NEVER:
pLdapPref->_dwChaseReferrals = (DWORD) (DWORD_PTR)LDAP_OPT_OFF;
break;
case ADS_CHASE_REFERRALS_SUBORDINATE:
pLdapPref->_dwChaseReferrals = LDAP_CHASE_SUBORDINATE_REFERRALS;
break;
case ADS_CHASE_REFERRALS_EXTERNAL:
pLdapPref->_dwChaseReferrals = LDAP_CHASE_EXTERNAL_REFERRALS;
break;
case ADS_CHASE_REFERRALS_ALWAYS:
pLdapPref->_dwChaseReferrals = (DWORD) (DWORD_PTR) LDAP_OPT_ON;
break;
}
break;
case ADS_SEARCHPREF_SORT_ON:
ReadSortingSupportedAttr(
pszLDAPServer,
&fSorting,
Credentials,
dwPort
) ;
if (!fSorting) {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
fWarning = TRUE;
continue;
}
//
// The value is actually a pointer to the LDAP Sort Key whose
// structure is as defined in the sort control RFC extension.
//
pSortKeyArray = (PADS_SORTKEY) pSearchPrefs[i].vValue.ProviderSpecific.lpValue;
dwSortKeyArrayLen = pSearchPrefs[i].vValue.ProviderSpecific.dwLength;
if (!pSortKeyArray || !dwSortKeyArrayLen ) {
continue;
}
if (dwSortKeyArrayLen % sizeof(ADS_SORTKEY) != 0 ) {
//
// The data given does not seem to contain a proper SortKey
// structure
//
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
fWarning = TRUE;
continue;
}
nKeys = dwSortKeyArrayLen / sizeof(ADS_SORTKEY);
if (pLdapPref->_pSortKeys) {
//
// Free the previous one
//
FreeSortKeys(pLdapPref->_pSortKeys, pLdapPref->_nSortKeys);
}
pLdapPref->_pSortKeys = (PLDAPSortKey) AllocADsMem(
sizeof(LDAPSortKey) * nKeys);
if (!pLdapPref->_pSortKeys) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
for (j=0; j<nKeys; j++) {
pLdapPref->_pSortKeys[j].sk_attrtype =
AllocADsStr(pSortKeyArray[j].pszAttrType);
pLdapPref->_pSortKeys[j].sk_matchruleoid =
pSortKeyArray[j].pszReserved;
pLdapPref->_pSortKeys[j].sk_reverseorder =
pSortKeyArray[j].fReverseorder;
}
pLdapPref->_nSortKeys = nKeys;
break;
case ADS_SEARCHPREF_CACHE_RESULTS:
// if doing a VLV search and user tries to turn on caching, reject
if (pLdapPref->_pVLVInfo && pSearchPrefs[i].vValue.Boolean) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
pLdapPref->_fCacheResults = pSearchPrefs[i].vValue.Boolean;
fSetCaching = TRUE; // set so if we later determine user wants to
// do a VLV search, we can reject if user tried
// to explicitly turn on caching
break;
//
// Like paged, setting this preference will mean that we use it
// by default, it is not used.
//
case ADS_SEARCHPREF_DIRSYNC:
if (pLdapPref->_dwPageSize) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
pLdapPref->_fDirSync = TRUE;
//
// Check if previous value is set and free if necessary.
//
if (pLdapPref->_pProvSpecific) {
if (pLdapPref->_pProvSpecific->lpValue) {
FreeADsMem(pLdapPref->_pProvSpecific->lpValue);
pLdapPref->_pProvSpecific->lpValue = NULL;
}
FreeADsMem(pLdapPref->_pProvSpecific);
pLdapPref->_pProvSpecific = NULL;
}
//
// Copy over the info here.
//
pLdapPref->_pProvSpecific =
(PADS_PROV_SPECIFIC) AllocADsMem(sizeof(ADS_PROV_SPECIFIC));
if (!pLdapPref->_pProvSpecific) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pLdapPref->_pProvSpecific->dwLength =
pSearchPrefs[i].vValue.ProviderSpecific.dwLength;
//
// If the octet string is anything other than NULL,
// we need to copy it over. If it is NULL, then this is
// the first time the control is being used.
//
if (pLdapPref->_pProvSpecific->dwLength > 0) {
pLdapPref->_pProvSpecific->lpValue =
(PBYTE)AllocADsMem(pLdapPref->_pProvSpecific->dwLength);
if (!pLdapPref->_pProvSpecific->lpValue) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
memcpy(
pLdapPref->_pProvSpecific->lpValue,
pSearchPrefs[i].vValue.ProviderSpecific.lpValue,
pLdapPref->_pProvSpecific->dwLength
);
}
break;
case ADS_SEARCHPREF_TOMBSTONE :
pLdapPref->_fTombStone = pSearchPrefs[i].vValue.Boolean;
break;
case ADS_SEARCHPREF_VLV:
// If user tried to explicitly turn on caching, reject
// Later on, we'll turn off caching
if ( fSetCaching && pLdapPref->_fCacheResults) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
// test for server support of VLV
ReadVLVSupportedAttr(
pszLDAPServer,
&fVLV,
Credentials,
dwPort
) ;
if (!fVLV) {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
fWarning = TRUE;
continue;
}
// basic sanity checks of user-supplied data
pVLV = (PADS_VLV) pSearchPrefs[i].vValue.ProviderSpecific.lpValue;
dwVLVLen = pSearchPrefs[i].vValue.ProviderSpecific.dwLength;
if (!pVLV || !dwVLVLen ) {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
fWarning = TRUE;
continue;
}
if (dwVLVLen != sizeof(ADS_VLV)) {
//
// The data given does not seem to contain a proper VLV
// structure
//
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
fWarning = TRUE;
continue;
}
// free the previous LDAPVLVInfo, if one exists (generally, it shouldn't)
if (pLdapPref->_pVLVInfo)
FreeLDAPVLVInfo(pLdapPref->_pVLVInfo);
// Copy the user's VLV search preferences into a LDAPVLVInfo
// Note that we copy the dwOffset and dwContentCount even if the user
// wants to do a greaterThanOrEqual-type VLV search. This is because
// we'll ignore those members if ldvlv_attrvalue != NULL.
pLdapPref->_pVLVInfo = (PLDAPVLVInfo) AllocADsMem(sizeof(LDAPVLVInfo));
if (!pLdapPref->_pVLVInfo)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pLdapPref->_pVLVInfo->ldvlv_version = LDAP_VLVINFO_VERSION; // 1
pLdapPref->_pVLVInfo->ldvlv_before_count = pVLV->dwBeforeCount;
pLdapPref->_pVLVInfo->ldvlv_after_count = pVLV->dwAfterCount;
pLdapPref->_pVLVInfo->ldvlv_offset = pVLV->dwOffset;
pLdapPref->_pVLVInfo->ldvlv_count = pVLV->dwContentCount;
pLdapPref->_pVLVInfo->ldvlv_attrvalue = NULL;
pLdapPref->_pVLVInfo->ldvlv_context = NULL;
pLdapPref->_pVLVInfo->ldvlv_extradata = NULL;
// copy the greaterThanOrEqual attribute, if provided by the user
if (pVLV->pszTarget) {
pLdapPref->_pVLVInfo->ldvlv_attrvalue = (PBERVAL) AllocADsMem(sizeof(BERVAL));
if (!pLdapPref->_pVLVInfo->ldvlv_attrvalue)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
// convert Unicode to UDF-8
// important: do NOT include the NULL terminator in the LDAPVLVInfo.ldvlv_attrvalue
hr = UnicodeToUTF8String(pVLV->pszTarget, &pUTF8Target);
BAIL_ON_FAILURE(hr);
// we want the number of bytes, not the number of MBCS characters
dwUTF8TargetLen = strlen(pUTF8Target);
pLdapPref->_pVLVInfo->ldvlv_attrvalue->bv_len = dwUTF8TargetLen;
pLdapPref->_pVLVInfo->ldvlv_attrvalue->bv_val = (PCHAR) AllocADsMem(dwUTF8TargetLen);
if (!pLdapPref->_pVLVInfo->ldvlv_attrvalue->bv_val)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
memcpy(pLdapPref->_pVLVInfo->ldvlv_attrvalue->bv_val,
pUTF8Target,
dwUTF8TargetLen);
}
// copy the context ID, if provided by the user
if (pVLV->lpContextID && pVLV->dwContextIDLength) {
pLdapPref->_pVLVInfo->ldvlv_context = (PBERVAL) AllocADsMem(sizeof(BERVAL));
if (pLdapPref->_pVLVInfo->ldvlv_context == NULL)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pLdapPref->_pVLVInfo->ldvlv_context->bv_val = (PCHAR) AllocADsMem(pVLV->dwContextIDLength);
if (pLdapPref->_pVLVInfo->ldvlv_context->bv_val == NULL)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pLdapPref->_pVLVInfo->ldvlv_context->bv_len = pVLV->dwContextIDLength;
memcpy(pLdapPref->_pVLVInfo->ldvlv_context->bv_val,
pVLV->lpContextID,
pVLV->dwContextIDLength);
}
// disable caching, since it's not supported in conjunction with VLV
pLdapPref->_fCacheResults = FALSE;
break;
case ADS_SEARCHPREF_ATTRIBUTE_QUERY:
// If user tried to explicitly set scope to other than "base", reject
// Later on, we'll set it to base.
if ( fSetScope && pLdapPref->_dwSearchScope != LDAP_SCOPE_BASE) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
// test for server support of attribute-scoped query
ReadAttribScopedSupportedAttr(
pszLDAPServer,
&fAttribScoped,
Credentials,
dwPort
) ;
if (!fAttribScoped) {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
fWarning = TRUE;
continue;
}
// basic sanity checks of user-supplied data
pAttribScoped = pSearchPrefs[i].vValue.CaseIgnoreString;
if (!pAttribScoped) {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
fWarning = TRUE;
continue;
}
// free the previous ADS_ATTRIBUTE_QUERY, if one exists (generally, it shouldn't)
if (pLdapPref->_pAttribScoped) {
FreeADsStr(pLdapPref->_pAttribScoped);
pLdapPref->_pAttribScoped = NULL;
}
// copy the ADS_ATTRIBUTE_QUERY
pLdapPref->_pAttribScoped = AllocADsStr(pAttribScoped);
if (!(pLdapPref->_pAttribScoped)) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
// set search scope to base (only scope supported by attrib-scoped query)
pLdapPref->_dwSearchScope = LDAP_SCOPE_BASE;
break;
case ADS_SEARCHPREF_SECURITY_MASK:
//
// test for server support of security descriptor control
//
ReadSecurityDescriptorControlType(
pszLDAPServer,
&dwSecDescType,
Credentials,
dwPort
);
if (dwSecDescType != ADSI_LDAPC_SECDESC_NT) {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
fWarning = TRUE;
continue;
}
//
// sanity check of user supplied data
//
dwSecurityMask = pSearchPrefs[i].vValue.Integer;
if (dwSecurityMask > (OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION)) {
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
fWarning = TRUE;
continue;
}
//
// enable the option
//
pLdapPref->_fSecurityDescriptorControl = TRUE;
pLdapPref->_SecurityDescriptorMask = static_cast<SECURITY_INFORMATION>(dwSecurityMask);
break;
default:
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
fWarning = TRUE;
continue;
}
}
error:
if (pUTF8Target)
FreeADsMem(pUTF8Target);
//
// Need to return the hr if it was something like out of mem.
// Most often though it will be either S_OK or s-error ocurred.
//
if (FAILED(hr)) {
//
// Free sort keys and dirsync data if applicable
//
if (pLdapPref->_pSortKeys) {
FreeSortKeys(pLdapPref->_pSortKeys, pLdapPref->_nSortKeys);
pLdapPref->_pSortKeys = NULL;
pLdapPref->_nSortKeys = 0;
}
if (pLdapPref->_pProvSpecific) {
if (pLdapPref->_pProvSpecific->lpValue) {
FreeADsMem(pLdapPref->_pProvSpecific->lpValue);
}
FreeADsMem(pLdapPref->_pProvSpecific);
pLdapPref->_pProvSpecific = NULL;
}
//
// Free VLV information if applicable
//
if (pLdapPref->_pVLVInfo) {
FreeLDAPVLVInfo(pLdapPref->_pVLVInfo);
pLdapPref->_pVLVInfo = NULL;
}
//
// Free attrib-scoped query if applicable
//
if (pLdapPref->_pAttribScoped) {
FreeADsStr(pLdapPref->_pAttribScoped);
pLdapPref->_pAttribScoped = NULL;
}
RRETURN(hr);
}
RRETURN (fWarning ? S_ADS_ERRORSOCCURRED : S_OK);
}
HRESULT
ADsExecuteSearch(
IN LDAP_SEARCH_PREF LdapPref,
IN LPWSTR pszADsPath,
IN LPWSTR pszLdapServer,
IN LPWSTR pszLdapDn,
IN LPWSTR pszSearchFilter,
IN LPWSTR * pAttributeNames,
IN DWORD dwNumberAttributes,
OUT PADS_SEARCH_HANDLE phSearchHandle
)
{
PLDAP_SEARCHINFO phSearchInfo = NULL;
LPWSTR szCurrAttr = NULL;
DWORD dwAttrNamesLen = 0;
HRESULT hr = S_OK;
ULONG i, j;
LPWSTR pszAttrNameBuffer = NULL, *ppszAttrs = NULL;
OBJECTINFO ObjectInfo;
POBJECTINFO pObjectInfo = &ObjectInfo;
//
// Initilize so that we wont end up freeing bad data.
//
memset(pObjectInfo, 0, sizeof(OBJECTINFO));
if (!phSearchHandle ) {
RRETURN (E_ADS_BAD_PARAMETER);
}
//
// Allocate search handle
//
phSearchInfo = (PLDAP_SEARCHINFO) AllocADsMem(sizeof(LDAP_SEARCHINFO));
if(!phSearchInfo)
BAIL_ON_FAILURE (hr = E_OUTOFMEMORY);
memset(phSearchInfo, 0, sizeof(LDAP_SEARCHINFO));
phSearchInfo->_pszADsPathContext = AllocADsStr(pszADsPath);
if(!(phSearchInfo->_pszADsPathContext))
BAIL_ON_FAILURE (hr = E_OUTOFMEMORY);
if (pszSearchFilter) {
phSearchInfo->_pszSearchFilter = AllocADsStr(pszSearchFilter);
}
else {
phSearchInfo->_pszSearchFilter = AllocADsStr(L"(objectClass=*)");
}
if(!(phSearchInfo->_pszSearchFilter))
BAIL_ON_FAILURE (hr = E_OUTOFMEMORY);
if (pszLdapServer) {
phSearchInfo->_pszLdapServer = AllocADsStr(pszLdapServer);
if (!phSearchInfo->_pszLdapServer) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
}
if (pszLdapDn) {
phSearchInfo->_pszBindContextDn = AllocADsStr(pszLdapDn);
if(!(phSearchInfo->_pszBindContextDn)){
BAIL_ON_FAILURE (hr = E_OUTOFMEMORY);
}
}
phSearchInfo->_fADsPathPresent = FALSE;
phSearchInfo->_fADsPathReturned = FALSE;
phSearchInfo->_fADsPathOnly = FALSE;
if (dwNumberAttributes == -1) {
//
// Specifies returning all attributes
//
phSearchInfo->_ppszAttrs = NULL;
phSearchInfo->_pszAttrNameBuffer = NULL;
phSearchInfo->_fADsPathPresent = TRUE;
}
else {
ppszAttrs = (LPWSTR *) AllocADsMem(
sizeof(LPWSTR) *
(dwNumberAttributes + 1)
);
if (!ppszAttrs)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
for (i = 0; i < dwNumberAttributes; i++)
dwAttrNamesLen+= (wcslen(pAttributeNames[i]) + 1) * sizeof(WCHAR);
pszAttrNameBuffer = (LPWSTR) AllocADsMem(
dwAttrNamesLen
);
if (!pszAttrNameBuffer)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
szCurrAttr = pszAttrNameBuffer;
for (i = 0, j = 0; i < dwNumberAttributes; i++) {
wcscpy(szCurrAttr, pAttributeNames[i]);
ppszAttrs[j] = szCurrAttr;
szCurrAttr += wcslen(ppszAttrs[j]) + 1;
if(_wcsicmp(ppszAttrs[j], L"ADsPath") == 0) {
//
// This attribute need not be sent
//
phSearchInfo->_fADsPathPresent = TRUE;
}
else {
j++;
}
}
// If the query requests only ADsPath, then set ppszAttrs[0] to some
// attribute. Setting it to NULL results in all attributes being
// returned, which is a huge performance hit. Instead, request only
// the objectclass (guaranteed to be present on all LDAP servers).
if (0 == j)
{
FreeADsMem(pszAttrNameBuffer);
pszAttrNameBuffer = (LPWSTR) AllocADsStr(L"objectClass");
if(pszAttrNameBuffer == NULL) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
FreeADsMem(ppszAttrs);
ppszAttrs = (LPWSTR *) AllocADsMem(sizeof(LPWSTR) * 2);
if(ppszAttrs == NULL) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
ppszAttrs[0] = pszAttrNameBuffer;
ppszAttrs[1] = NULL;
phSearchInfo->_fADsPathOnly = TRUE;
}
else
ppszAttrs[j] = NULL;
phSearchInfo->_ppszAttrs = ppszAttrs;
phSearchInfo->_pszAttrNameBuffer = pszAttrNameBuffer;
}
hr = ADsObject(pszADsPath, pObjectInfo);
BAIL_ON_FAILURE(hr);
phSearchInfo->_dwPort = pObjectInfo->PortNumber;
phSearchInfo->_pConnection = NULL;
phSearchInfo->_hPagedSearch = NULL;
phSearchInfo->_pSearchResults = NULL;
phSearchInfo->_cSearchResults = 0;
phSearchInfo->_dwCurrResult = 0;
phSearchInfo->_dwMaxResultGot = 0;
phSearchInfo->_currMsgId = (DWORD) -1;
phSearchInfo->_pCurrentRow = NULL;
phSearchInfo->_pFirstAttr = NULL;
phSearchInfo->_fLastResult = FALSE;
phSearchInfo->_fLastPage = FALSE;
phSearchInfo->_fBefFirstRow = TRUE;
phSearchInfo->_hrLastSearch = S_OK;
phSearchInfo->_fNonFatalErrors = FALSE;
phSearchInfo->_fAbandon = FALSE;
phSearchInfo->_dwVLVOffset = 0;
phSearchInfo->_dwVLVCount = 0;
phSearchInfo->_pVLVContextID = NULL;
phSearchInfo->_pBerValAttribScoped = NULL;
//
// Copy the search preference structure and also make a copy of the memory
// that the internal pointers point to. This can probably be done better by
// an assignment operator.
//
phSearchInfo->_SearchPref = LdapPref;
phSearchInfo->_SearchPref._pVLVInfo = NULL;
phSearchInfo->_SearchPref._pAttribScoped = NULL;
// sorting
if (LdapPref._pSortKeys) {
phSearchInfo->_SearchPref._pSortKeys = (PLDAPSortKey) AllocADsMem(
sizeof(LDAPSortKey) * LdapPref._nSortKeys);
if (!phSearchInfo->_SearchPref._pSortKeys) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
memcpy(
phSearchInfo->_SearchPref._pSortKeys,
LdapPref._pSortKeys,
sizeof(LDAPSortKey) * LdapPref._nSortKeys
);
//
// We need to copy over all the attibutes.
//
for (i=0; i < LdapPref._nSortKeys; i++) {
phSearchInfo->_SearchPref._pSortKeys[i].sk_attrtype =
AllocADsStr( LdapPref._pSortKeys[i].sk_attrtype);
if (!phSearchInfo->_SearchPref._pSortKeys[i].sk_attrtype) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
}
}
// VLV
if (LdapPref._pVLVInfo) {
hr = CopyLDAPVLVInfo(LdapPref._pVLVInfo, &(phSearchInfo->_SearchPref._pVLVInfo));
BAIL_ON_FAILURE(hr);
}
// Attribute-scoped query
if (LdapPref._pAttribScoped) {
phSearchInfo->_SearchPref._pAttribScoped = AllocADsStr(LdapPref._pAttribScoped);
if (!(phSearchInfo->_SearchPref._pAttribScoped)) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
}
*phSearchHandle = phSearchInfo;
FreeObjectInfo(pObjectInfo);
RRETURN(S_OK);
error:
FreeObjectInfo(pObjectInfo);
if(phSearchInfo) {
if (phSearchInfo->_pszLdapServer) {
FreeADsStr(phSearchInfo->_pszLdapServer);
}
if(phSearchInfo->_pszBindContextDn)
FreeADsStr(phSearchInfo->_pszBindContextDn);
if(phSearchInfo->_pszADsPathContext)
FreeADsStr(phSearchInfo->_pszADsPathContext);
if(phSearchInfo->_pszSearchFilter)
FreeADsStr(phSearchInfo->_pszSearchFilter);
if(phSearchInfo->_ppszAttrs)
FreeADsMem(phSearchInfo->_ppszAttrs);
else if(ppszAttrs)
FreeADsMem(ppszAttrs);
if(phSearchInfo->_pszAttrNameBuffer)
FreeADsMem(phSearchInfo->_pszAttrNameBuffer);
else if(pszAttrNameBuffer)
FreeADsMem(pszAttrNameBuffer);
if (phSearchInfo->_SearchPref._pVLVInfo) {
FreeLDAPVLVInfo(phSearchInfo->_SearchPref._pVLVInfo);
}
if (phSearchInfo->_SearchPref._pAttribScoped) {
FreeADsStr(phSearchInfo->_SearchPref._pAttribScoped);
}
FreeADsMem(phSearchInfo);
}
RRETURN (hr);
}
HRESULT
ADsAbandonSearch(
IN ADS_SEARCH_HANDLE hSearchHandle
)
{
PLDAP_SEARCHINFO phSearchInfo = (PLDAP_SEARCHINFO) hSearchHandle;
DWORD dwStatus = 0;
// If there is an outstanding message id, call LdapAbandon
//
if (phSearchInfo->_currMsgId != (DWORD) -1) {
dwStatus = LdapAbandon(
phSearchInfo->_pConnection,
phSearchInfo->_currMsgId
);
}
//
// Make sure that we don't do extra searches anymore. We only give back
// whatever rows we have already received from the server
//
phSearchInfo->_fAbandon = TRUE;
RRETURN(S_OK);
}
HRESULT
ADsCloseSearchHandle (
IN ADS_SEARCH_HANDLE hSearchHandle
)
{
HRESULT hr = S_OK;
PLDAP_SEARCHINFO phSearchInfo = (PLDAP_SEARCHINFO) hSearchHandle;
DWORD i=0;
if (!phSearchInfo)
RRETURN (E_ADS_BAD_PARAMETER);
if (phSearchInfo->_pszLdapServer)
FreeADsStr(phSearchInfo->_pszLdapServer);
if(phSearchInfo->_pszBindContextDn)
FreeADsStr(phSearchInfo->_pszBindContextDn);
if(phSearchInfo->_pszADsPathContext)
FreeADsStr(phSearchInfo->_pszADsPathContext);
if(phSearchInfo->_pszSearchFilter)
FreeADsStr(phSearchInfo->_pszSearchFilter);
if(phSearchInfo->_ppszAttrs)
FreeADsMem(phSearchInfo->_ppszAttrs);
if(phSearchInfo->_pszAttrNameBuffer)
FreeADsMem(phSearchInfo->_pszAttrNameBuffer);
if (phSearchInfo->_pSearchResults) {
for (DWORD i=0; i <= phSearchInfo->_dwMaxResultGot; i++) {
LdapMsgFree(
phSearchInfo->_pSearchResults[i]
);
}
FreeADsMem(phSearchInfo->_pSearchResults);
}
if (phSearchInfo->_hPagedSearch) {
LdapSearchAbandonPage( phSearchInfo->_pConnection,
phSearchInfo->_hPagedSearch
);
}
//
// Free ServerControls
//
if (phSearchInfo->_ServerControls) {
//
// This code assumes there are only VLV, sort, dirsync,
// security descriptor & attribute-scoped query controls.
// If more controls are added, modify the code accordingly.
// Nothing needs to be freed for the dirsync control for now.
// Freeing the searchpref will take care of the controls data.
//
for (i=0; phSearchInfo->_ServerControls[i]; i++) {
BOOL fSortControl = FALSE;
BOOL fVLVControl = FALSE;
BOOL fSecDescControl = FALSE;
if (phSearchInfo->_ServerControls[i]->ldctl_oid) {
// Compare with sort control
if (wcscmp(
phSearchInfo->_ServerControls[i]->ldctl_oid,
LDAP_SERVER_SORT_OID_W
) == 0 ) {
fSortControl = TRUE;
} else {
fSortControl = FALSE;
}
// Compare with VLV control
if (wcscmp(
phSearchInfo->_ServerControls[i]->ldctl_oid,
LDAP_CONTROL_VLVREQUEST_W
) == 0 ) {
fVLVControl = TRUE;
} else {
fVLVControl = FALSE;
}
// Compare with security descriptor control
if (wcscmp(
phSearchInfo->_ServerControls[i]->ldctl_oid,
LDAP_SERVER_SD_FLAGS_OID_W
) == 0 ) {
fSecDescControl = TRUE;
} else {
fSecDescControl = FALSE;
}
}
//
// Free the sort control if this is it.
//
if (fSortControl) {
if (phSearchInfo->_ServerControls[i]->ldctl_oid != NULL) {
ldap_memfree( phSearchInfo->_ServerControls[i]->ldctl_oid );
}
if (phSearchInfo->_ServerControls[i]->ldctl_value.bv_val != NULL) {
ldap_memfreeA( phSearchInfo->_ServerControls[i]->ldctl_value.bv_val );
}
}
//
// Free the security descriptor control if this is it
//
if (fSecDescControl) {
if (phSearchInfo->_ServerControls[i]->ldctl_value.bv_val != NULL) {
FreeADsMem( phSearchInfo->_ServerControls[i]->ldctl_value.bv_val );
}
}
//
// Need to free the control if sort or not.
// Exception: VLV control is freed with LdapControlFree,
// not with FreeADsMem
//
if (fVLVControl) {
LdapControlFree( phSearchInfo->_ServerControls[i] );
}
else {
FreeADsMem( phSearchInfo->_ServerControls[i] );
}
}
FreeADsMem( phSearchInfo->_ServerControls );
}
//
// Free SearchPref data
//
FreeSortKeys(
phSearchInfo->_SearchPref._pSortKeys,
phSearchInfo->_SearchPref._nSortKeys
);
FreeLDAPVLVInfo(phSearchInfo->_SearchPref._pVLVInfo);
if (phSearchInfo->_SearchPref._pAttribScoped)
FreeADsStr(phSearchInfo->_SearchPref._pAttribScoped);
//
// Free Dirsync control infromation.
//
if (phSearchInfo->_SearchPref._fDirSync) {
if (phSearchInfo->_SearchPref._pProvSpecific) {
//
// Free the data if applicable.
//
if (phSearchInfo->_SearchPref._pProvSpecific->lpValue) {
FreeADsMem(phSearchInfo->_SearchPref._pProvSpecific->lpValue);
}
//
// Free the struct itself
//
FreeADsMem(phSearchInfo->_SearchPref._pProvSpecific);
phSearchInfo->_SearchPref._pProvSpecific = NULL;
}
}
if (phSearchInfo->_pVLVContextID) {
if (phSearchInfo->_pVLVContextID->bv_val) {
FreeADsMem(phSearchInfo->_pVLVContextID->bv_val);
}
FreeADsMem(phSearchInfo->_pVLVContextID);
}
if (phSearchInfo->_pBerVal) {
ber_bvfree(phSearchInfo->_pBerVal);
}
if (phSearchInfo->_pBerValAttribScoped) {
ber_bvfree(phSearchInfo->_pBerValAttribScoped);
}
if(phSearchInfo->_pConnection)
LdapCloseObject(
phSearchInfo->_pConnection
);
FreeADsMem(phSearchInfo);
RRETURN (hr);
}
HRESULT
ADsGetFirstRow(
IN ADS_SEARCH_HANDLE hSearchHandle,
IN CCredentials& Credentials
)
{
HRESULT hr = S_OK;
DWORD dwStatus = NO_ERROR, dwStatus2 = NO_ERROR;
PLDAP_SEARCHINFO phSearchInfo = (PLDAP_SEARCHINFO) hSearchHandle;
LDAP *ld = NULL;
BOOL fNewOptionSet = FALSE;
DWORD oldOption = 0;
DWORD newOption = 0;
if (!phSearchInfo) {
RRETURN(E_ADS_BAD_PARAMETER);
}
if(!phSearchInfo->_pConnection) {
hr = LdapOpenObject(
phSearchInfo->_pszLdapServer,
phSearchInfo->_pszBindContextDn,
&phSearchInfo->_pConnection,
Credentials,
phSearchInfo->_dwPort
);
BAIL_ON_FAILURE(hr);
//
// Set the preferences like deref aliases, time limit and size limit
//
if (ld = phSearchInfo->_pConnection->LdapHandle) {
ld->ld_deref = phSearchInfo->_SearchPref._dwDerefAliases;
ld->ld_sizelimit = phSearchInfo->_SearchPref._dwSizeLimit;
ld->ld_timelimit = phSearchInfo->_SearchPref._dwTimeLimit;
}
hr = AddSearchControls(phSearchInfo, Credentials);
BAIL_ON_FAILURE(hr);
}
ld = phSearchInfo->_pConnection->LdapHandle;
dwStatus = ldap_get_option(
ld,
LDAP_OPT_REFERRALS,
&(oldOption)
);
newOption = phSearchInfo->_SearchPref._dwChaseReferrals;
dwStatus2 = ldap_set_option(
ld,
LDAP_OPT_REFERRALS,
&(newOption)
);
if (dwStatus == NO_ERROR && dwStatus2 == NO_ERROR)
fNewOptionSet = TRUE;
if(!phSearchInfo->_pSearchResults) {
//
// Get the results. This function uses various members of pSearchInfo
// and returns the result in phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
// which can be used to call LdapFirstEntry and LdapNextEntry
//
hr = ADsGetResults(
phSearchInfo
);
//
// Update the VLV server response if applicable
//
if (phSearchInfo->_SearchPref._pVLVInfo
&& phSearchInfo->_pSearchResults
&& phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) {
HRESULT hrVLV = S_OK;
hrVLV = StoreVLVInfo(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
//
// we only care if storing the cookie failed
// if the search otherwise succeeded
//
if (FAILED(hrVLV) && SUCCEEDED(hr)) {
hr = hrVLV;
}
}
//
// Update the Attribute-Scoped Query info if applicable
//
if (phSearchInfo->_SearchPref._pAttribScoped
&& phSearchInfo->_pSearchResults
&& phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) {
HRESULT hrASQ = S_OK;
hrASQ = StoreAttribScopedInfo(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
//
// we only care if storing the info failed
// if the search otherwise succeeded
//
if (FAILED(hrASQ) && SUCCEEDED(hr)) {
hr = hrASQ;
}
}
//
// Update the dirsync control cookie if applicable.
//
if (phSearchInfo->_SearchPref._fDirSync
&& phSearchInfo->_pSearchResults
&& phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) {
//
// Store the cookie info in searchprefs.
//
StoreDirSyncCookie(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
while (hr == S_ADS_NOMORE_ROWS) {
//
// Try and get more results - this will handle the
// case of the DirSync cookie indicating more rows
// correctly.
//
hr = ADsGetMoreResultsDirSync(
phSearchInfo,
Credentials
);
BAIL_ON_FAILURE(hr);
}
}
if (FAILED(hr) || hr == S_ADS_NOMORE_ROWS) {
goto error;
}
}
//
// Need to set the cur result to 0 as it maybe a case
// where we are now going back to the top of a multiple
// page search. This will make no difference if the cache
// is turned off.
//
phSearchInfo->_dwCurrResult = 0;
hr = LdapFirstEntry(
phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[0],
&phSearchInfo->_pCurrentRow
);
BAIL_ON_FAILURE(hr);
if(phSearchInfo->_pCurrentRow) {
phSearchInfo->_pFirstAttr = NULL;
phSearchInfo->_fBefFirstRow = FALSE;
hr = S_OK;
}
else {
hr = S_ADS_NOMORE_ROWS;
//
// Might be DirSync case where we need to try fetch
// more results.
//
if (phSearchInfo->_SearchPref._fDirSync
&& phSearchInfo->_fMoreDirSync ) {
//
// Try and get more results - this will handle the
// case of the DirSync cookie indicating more rows
// correctly.
//
hr = ADsGetMoreResultsDirSync(
phSearchInfo,
Credentials
);
BAIL_ON_FAILURE(hr);
}
}
error:
if (ld && fNewOptionSet) {
ldap_set_option(
ld,
LDAP_OPT_REFERRALS,
&(oldOption)
);
}
//
// When there is no more rows to be returned, return whatever error
// that was returned from the last search
//
if (hr == S_ADS_NOMORE_ROWS) {
if (phSearchInfo->_hrLastSearch != S_OK) {
RRETURN(phSearchInfo->_hrLastSearch);
}
else if (phSearchInfo->_fNonFatalErrors) {
RRETURN(S_ADS_ERRORSOCCURRED);
}
else {
RRETURN(hr);
}
}
else {
RRETURN(hr);
}
}
HRESULT
ADsGetNextRow(
IN ADS_SEARCH_HANDLE hSearchHandle,
IN CCredentials& Credentials
)
{
HRESULT hr = S_OK;
DWORD dwStatus = NO_ERROR, dwStatus2 = NO_ERROR;
PLDAP_SEARCHINFO phSearchInfo = (PLDAP_SEARCHINFO) hSearchHandle;
LDAP *ld = NULL;
BOOL fNewOptionSet = FALSE;
DWORD oldOption = 0;
DWORD newOption = 0;
if (!phSearchInfo) {
RRETURN(E_ADS_BAD_PARAMETER);
}
if(!phSearchInfo->_pConnection) {
hr = LdapOpenObject(
phSearchInfo->_pszLdapServer,
phSearchInfo->_pszBindContextDn,
&phSearchInfo->_pConnection,
Credentials,
phSearchInfo->_dwPort
);
BAIL_ON_FAILURE(hr);
//
// Set the preferences like deref aliases, time limit and size limit
//
if (ld = phSearchInfo->_pConnection->LdapHandle) {
ld->ld_deref = phSearchInfo->_SearchPref._dwDerefAliases;
ld->ld_sizelimit = phSearchInfo->_SearchPref._dwSizeLimit;
ld->ld_timelimit = phSearchInfo->_SearchPref._dwTimeLimit;
}
hr = AddSearchControls(phSearchInfo, Credentials);
BAIL_ON_FAILURE(hr);
}
ld = phSearchInfo->_pConnection->LdapHandle;
dwStatus = ldap_get_option(
ld,
LDAP_OPT_REFERRALS,
&(oldOption)
);
newOption = phSearchInfo->_SearchPref._dwChaseReferrals;
dwStatus2 = ldap_set_option(
ld,
LDAP_OPT_REFERRALS,
&(newOption)
);
if (dwStatus == NO_ERROR && dwStatus2 == NO_ERROR)
fNewOptionSet = TRUE;
if(!phSearchInfo->_pSearchResults) {
//
// Get the results. This function uses various members of pSearchInfo
// and returns the result in phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
// which can be used to call LdapFirstEntry and LdapNextEntry
//
hr = ADsGetResults(
phSearchInfo
);
//
// Update the dirsync control cookie if applicable.
//
if (phSearchInfo->_SearchPref._fDirSync
&& phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) {
//
// Store the cookie info in searchprefs.
//
StoreDirSyncCookie(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
}
//
// Update the VLV server response if applicable
//
if (phSearchInfo->_SearchPref._pVLVInfo
&& phSearchInfo->_pSearchResults
&& phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) {
HRESULT hrVLV = S_OK;
hrVLV = StoreVLVInfo(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
//
// we only care if storing the cookie failed if the search
// otherwise succeeded
//
if (FAILED(hrVLV) && SUCCEEDED(hr)) {
hr = hrVLV;
}
}
//
// Update the Attribute-Scoped Query info if applicable
//
if (phSearchInfo->_SearchPref._pAttribScoped
&& phSearchInfo->_pSearchResults
&& phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) {
HRESULT hrASQ = S_OK;
hrASQ = StoreAttribScopedInfo(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
//
// we only care if storing the info failed
// if the search otherwise succeeded
//
if (FAILED(hrASQ) && SUCCEEDED(hr)) {
hr = hrASQ;
}
}
if (hr == S_ADS_NOMORE_ROWS) {
//
// Try and get more results - this will handle the
// case of the DirSync cookie indicating more rows
// correctly.
//
hr = ADsGetMoreResultsDirSync(
phSearchInfo,
Credentials
);
BAIL_ON_FAILURE(hr);
}
if (FAILED(hr) || hr == S_ADS_NOMORE_ROWS) {
goto error;
}
}
//
// If the current row has not been obtained, get it now. Need to
// distinguish between the case where we are after the end of the result
// from the case where we are before the beginning of the result. In
// both cases _pCurrentRow is NULL, but _fBefFirstRow is TRUE only in
// the latter case.
//
if(!phSearchInfo->_pCurrentRow) {
if(phSearchInfo->_fBefFirstRow)
{
//
// Call the ldap specific function to get the first row.
//
hr = LdapFirstEntry(
phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&phSearchInfo->_pCurrentRow
);
}
}
else {
//
// Call the ldap specific function to get the next row
//
hr = LdapNextEntry(
phSearchInfo->_pConnection,
phSearchInfo->_pCurrentRow,
&phSearchInfo->_pCurrentRow
);
}
if (!phSearchInfo->_pCurrentRow
&& (phSearchInfo->_dwCurrResult < phSearchInfo->_dwMaxResultGot)) {
//
// In this case we need to proceed to the next result in memory
//
hr = S_OK;
while (!phSearchInfo->_pCurrentRow
&& phSearchInfo->_dwCurrResult < phSearchInfo->_dwMaxResultGot
&& SUCCEEDED(hr)) {
//
// With Dirsync we may have results that do not have
// any entries in them, but the next one may.
//
phSearchInfo->_dwCurrResult++;
hr = LdapFirstEntry(
phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&phSearchInfo->_pCurrentRow
);
}
}
else if (!phSearchInfo->_pCurrentRow
&& (!phSearchInfo->_fLastResult
|| !phSearchInfo->_fLastPage
|| phSearchInfo->_fMoreDirSync )
) {
//
// Now if both lastResult and page are true, we need to call
// ADsGetMoreResultsDirSync. This is the unusual case.
//
if (phSearchInfo->_fLastResult && phSearchInfo->_fLastPage) {
hr = ADsGetMoreResultsDirSync(
phSearchInfo,
Credentials
);
}
else {
//
// This means that there are more results to be obtained
// although the current result has reached its end.
// Call the function to get more results in
// phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
// It will increment _dwCurrResult and reallocate if needed.
//
hr = ADsGetMoreResults(
phSearchInfo
);
}
//
// In async searches, we will know if we have to call
// ADsGetMoreResultsAsync only now. So recheck.
//
if (hr == S_ADS_NOMORE_ROWS
&& phSearchInfo->_SearchPref._fAsynchronous
) {
if (phSearchInfo->_fLastResult && phSearchInfo->_fLastPage) {
hr = ADsGetMoreResultsDirSync(
phSearchInfo,
Credentials
);
}
}
//
// Update the dirsync control cookie if applicable.
//
if (phSearchInfo->_SearchPref._fDirSync
&& phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) {
//
// Store the cookie info in searchprefs.
//
StoreDirSyncCookie(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
}
//
// Update the Attribute-Scoped Query info if applicable
//
if (phSearchInfo->_SearchPref._pAttribScoped
&& phSearchInfo->_pSearchResults
&& phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) {
HRESULT hrASQ = S_OK;
hrASQ = StoreAttribScopedInfo(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
//
// we only care if storing the info failed
// if the search otherwise succeeded
//
if (FAILED(hrASQ) && SUCCEEDED(hr)) {
hr = hrASQ;
}
}
if (FAILED(hr) || hr == S_ADS_NOMORE_ROWS) {
goto error;
}
hr = LdapFirstEntry(
phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&phSearchInfo->_pCurrentRow
);
if (!phSearchInfo->_pCurrentRow) {
//
// This means that we can possibly have more results
// but this time we did not get any - not yet a NULL
// cookie on a paged search that is.
//
ADsSetLastError(
ERROR_MORE_DATA,
L"Calling GetNextRow can potentially return more results.",
L"LDAP Provider"
);
}
}
BAIL_ON_FAILURE(hr);
if(phSearchInfo->_pCurrentRow) {
phSearchInfo->_pFirstAttr = NULL;
phSearchInfo->_fBefFirstRow = FALSE;
hr = S_OK;
}
else {
hr = S_ADS_NOMORE_ROWS;
//
// Might be DirSync case where we need to try fetch
// more results.
//
if (phSearchInfo->_SearchPref._fDirSync
&& phSearchInfo->_fMoreDirSync ) {
//
// Try and get more results - this will handle the
// case of the DirSync cookie indicating more rows
// correctly.
//
hr = ADsGetMoreResultsDirSync(
phSearchInfo,
Credentials
);
BAIL_ON_FAILURE(hr);
}
}
error:
if (ld && fNewOptionSet) {
ldap_set_option(
ld,
LDAP_OPT_REFERRALS,
&(oldOption)
);
}
//
// When there is no more rows to be returned, return whatever error
// that was returned from the last search
//
if (hr == S_ADS_NOMORE_ROWS) {
if (phSearchInfo->_hrLastSearch != S_OK) {
RRETURN(phSearchInfo->_hrLastSearch);
}
else if (phSearchInfo->_fNonFatalErrors) {
RRETURN(S_ADS_ERRORSOCCURRED);
}
else {
RRETURN(hr);
}
}
else {
RRETURN(hr);
}
}
//
// Function to get the results by performing the appropriate search depending
// on the preferences; Paged Results, Sorting, Synchronous, Asynchrnous or
// Timed Synchronous
//
HRESULT
ADsGetResults(
IN PLDAP_SEARCHINFO phSearchInfo
)
{
HRESULT hr = S_OK;
DWORD dwError;
WCHAR pszErrorBuf[MAX_PATH], pszNameBuf[MAX_PATH];
ULONG totalCount;
int resType;
ADsAssert(phSearchInfo);
//
// If abandon has been called, we don't get more results.
//
if (phSearchInfo->_fAbandon) {
RRETURN(S_ADS_NOMORE_ROWS);
}
if(!phSearchInfo->_pSearchResults) {
//
// Allocate an array of handles to Search Results
//
phSearchInfo->_pSearchResults = (LDAPMessage **) AllocADsMem(
sizeof(LDAPMessage *) *
NO_LDAP_RESULT_HANDLES);
if(!phSearchInfo->_pSearchResults) {
hr = E_OUTOFMEMORY;
goto error;
}
phSearchInfo->_dwCurrResult = 0;
phSearchInfo->_cSearchResults = NO_LDAP_RESULT_HANDLES;
//
// Should also set the cur max to 0;
//
phSearchInfo->_dwMaxResultGot = 0;
}
// Initialize Result to NULL
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult] = NULL;
//
// Call the ldap specific search function
//
if (phSearchInfo->_SearchPref._dwPageSize != 0) {
//
// Paged results
//
if(!phSearchInfo->_hPagedSearch ) {
hr = LdapSearchInitPage(
phSearchInfo->_pConnection,
phSearchInfo->_pszBindContextDn,
phSearchInfo->_SearchPref._dwSearchScope,
phSearchInfo->_pszSearchFilter,
phSearchInfo->_ppszAttrs,
phSearchInfo->_SearchPref._fAttrsOnly,
phSearchInfo->_ServerControls,
phSearchInfo->_ClientControls,
phSearchInfo->_SearchPref._dwPagedTimeLimit,
phSearchInfo->_SearchPref._dwSizeLimit,
NULL,
&phSearchInfo->_hPagedSearch
);
BAIL_ON_FAILURE(hr);
}
if ( phSearchInfo->_SearchPref._fAsynchronous ) {
hr = LdapGetNextPage(
phSearchInfo->_pConnection,
phSearchInfo->_hPagedSearch,
phSearchInfo->_SearchPref._dwPageSize,
(ULONG *)&phSearchInfo->_currMsgId
);
if (FAILED(hr)) {
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) {
phSearchInfo->_fLastPage = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
} else {
BAIL_ON_FAILURE(hr);
}
}
//
// Wait for one page worth of results to get back.
//
hr = LdapResult(
phSearchInfo->_pConnection,
phSearchInfo->_currMsgId,
LDAP_MSG_ALL,
phSearchInfo->_SearchPref._timeout.tv_sec ?
&phSearchInfo->_SearchPref._timeout : NULL,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&resType
);
if (FAILED(hr)) {
if ((hr == HRESULT_FROM_WIN32(ERROR_DS_INVALID_DN_SYNTAX)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_NAMING_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_OBJ_CLASS_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_FILTER_UNKNOWN)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_PARAM_ERROR)) ||
(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER))) {
phSearchInfo->_fLastResult = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
}
else {
BAIL_ON_FAILURE(hr);
}
}
hr = LdapGetPagedCount(
phSearchInfo->_pConnection,
phSearchInfo->_hPagedSearch,
&totalCount,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
);
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) {
hr = S_OK;
}
//
// if hr failed, we will do the corresponding according to whether there is any result returned in our mechanism later
//
}
else {
hr = LdapGetNextPageS(
phSearchInfo->_pConnection,
phSearchInfo->_hPagedSearch,
phSearchInfo->_SearchPref._timeout.tv_sec ?
&phSearchInfo->_SearchPref._timeout : NULL,
phSearchInfo->_SearchPref._dwPageSize,
&totalCount,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
);
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) {
phSearchInfo->_fLastPage = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
}
//
// if hr failed, we will do the corresponding according to whether there is any result returned in our mechanism later
//
phSearchInfo->_fLastResult = TRUE;
}
}
else if (phSearchInfo->_SearchPref._fAsynchronous) {
hr = LdapSearchExt(
phSearchInfo->_pConnection,
phSearchInfo->_pszBindContextDn,
phSearchInfo->_SearchPref._dwSearchScope,
phSearchInfo->_pszSearchFilter,
phSearchInfo->_ppszAttrs,
phSearchInfo->_SearchPref._fAttrsOnly,
phSearchInfo->_ServerControls,
phSearchInfo->_ClientControls,
phSearchInfo->_SearchPref._dwPagedTimeLimit,
phSearchInfo->_SearchPref._dwSizeLimit,
&phSearchInfo->_currMsgId
);
BAIL_ON_FAILURE(hr);
//
// Wait for atleast one result
//
hr = LdapResult(
phSearchInfo->_pConnection,
phSearchInfo->_currMsgId,
LDAP_MSG_RECEIVED,
phSearchInfo->_SearchPref._timeout.tv_sec ?
&phSearchInfo->_SearchPref._timeout : NULL,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&resType
);
if ((hr == HRESULT_FROM_WIN32(ERROR_DS_INVALID_DN_SYNTAX)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_NAMING_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_OBJ_CLASS_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_FILTER_UNKNOWN)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_PARAM_ERROR)) ||
(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER))) {
phSearchInfo->_fLastResult = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
}
//
// if hr failed, we will do the corresponding according to whether there is any result returned in our mechanism later
//
phSearchInfo->_fLastPage = TRUE;
}
else if (phSearchInfo->_SearchPref._timeout.tv_sec != 0) {
hr = LdapSearchExtS(
phSearchInfo->_pConnection,
phSearchInfo->_pszBindContextDn,
phSearchInfo->_SearchPref._dwSearchScope,
phSearchInfo->_pszSearchFilter,
phSearchInfo->_ppszAttrs,
phSearchInfo->_SearchPref._fAttrsOnly,
phSearchInfo->_ServerControls,
phSearchInfo->_ClientControls,
&phSearchInfo->_SearchPref._timeout,
phSearchInfo->_SearchPref._dwSizeLimit,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
);
phSearchInfo->_fLastResult = TRUE;
phSearchInfo->_fLastPage = TRUE;
}
else {
hr = LdapSearchExtS(
phSearchInfo->_pConnection,
phSearchInfo->_pszBindContextDn,
phSearchInfo->_SearchPref._dwSearchScope,
phSearchInfo->_pszSearchFilter,
phSearchInfo->_ppszAttrs,
phSearchInfo->_SearchPref._fAttrsOnly,
phSearchInfo->_ServerControls,
phSearchInfo->_ClientControls,
NULL,
phSearchInfo->_SearchPref._dwSizeLimit,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
);
phSearchInfo->_fLastResult = TRUE;
phSearchInfo->_fLastPage = TRUE;
}
//
// Only if there are zero rows returned, return the error,
// otherwise, store the error and return when GetNextRow is
// called for the last time
//
if (FAILED(hr) &&
(LdapCountEntries( phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) == 0)) {
BAIL_ON_FAILURE(hr);
}
else {
phSearchInfo->_hrLastSearch = hr;
hr = S_OK;
}
phSearchInfo->_pCurrentRow = NULL;
error:
RRETURN(hr);
}
//
// For Asynchronous or paged searches, more results need to be obtained, once
// the current result set is exhausted. This function gets the next result set
// if one is available. In the case of paged results, this might translate to
// getting the next page.
//
HRESULT
ADsGetMoreResults(
IN PLDAP_SEARCHINFO phSearchInfo
)
{
HRESULT hr = S_OK;
DWORD dwError;
LPWSTR pszLDAPPath;
WCHAR pszErrorBuf[MAX_PATH], pszNameBuf[MAX_PATH];
ULONG totalCount;
int resType;
ADsAssert(phSearchInfo);
//
// If abandon has been called, we don't get more results.
//
if (phSearchInfo->_fAbandon) {
RRETURN(S_ADS_NOMORE_ROWS);
}
if (phSearchInfo->_fLastResult && phSearchInfo->_fLastPage)
RRETURN (S_ADS_NOMORE_ROWS);
if (phSearchInfo->_fLastResult == FALSE) {
//
// if we need to cache the results, then we save the result in the
// phSearchInfo->_pSearchResults array. If we don't need to cache it,
// release the result and save it in the same place.
if ( phSearchInfo->_SearchPref._fCacheResults ) {
ADsAssert(phSearchInfo->_dwCurrResult
== phSearchInfo->_dwMaxResultGot);
phSearchInfo->_dwCurrResult++;
phSearchInfo->_dwMaxResultGot++;
if (phSearchInfo->_dwCurrResult >= phSearchInfo->_cSearchResults) {
//
// Need to allocate more memory for handles
//
phSearchInfo->_pSearchResults = (LDAPMessage **) ReallocADsMem(
(void *) phSearchInfo->_pSearchResults,
sizeof(LDAPMessage *) *
phSearchInfo->_cSearchResults,
sizeof(LDAPMessage *) *
(phSearchInfo->_cSearchResults +
NO_LDAP_RESULT_HANDLES));
if(!phSearchInfo->_pSearchResults) {
hr = E_OUTOFMEMORY;
phSearchInfo->_dwCurrResult--;
phSearchInfo->_dwMaxResultGot--;
goto error;
}
phSearchInfo->_cSearchResults += NO_LDAP_RESULT_HANDLES;
}
}
else {
//
// Release and use the same space to store the next result.
//
LdapMsgFree(phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]);
}
// Initialize Result to NULL
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult] = NULL;
hr = LdapResult(
phSearchInfo->_pConnection,
phSearchInfo->_currMsgId,
LDAP_MSG_RECEIVED,
phSearchInfo->_SearchPref._timeout.tv_sec ?
&phSearchInfo->_SearchPref._timeout : NULL,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&resType
);
if ((hr == HRESULT_FROM_WIN32(ERROR_DS_INVALID_DN_SYNTAX)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_NAMING_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_OBJ_CLASS_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_FILTER_UNKNOWN)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_PARAM_ERROR)) ||
(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER))) {
phSearchInfo->_fLastResult = TRUE;
}
else {
phSearchInfo->_hrLastSearch = hr;
hr = S_OK;
RRETURN(hr);
}
}
//
// The last result has been reached. Check if we need to look for further
// pages
//
if ( phSearchInfo->_fLastPage == FALSE) {
//
// if we need to cache the results, then we save the result in the
// phSearchInfo->_pSearchResults array. If we don't need to cache it,
// release the result and save it in the same place.
if ( phSearchInfo->_SearchPref._fCacheResults ) {
ADsAssert(phSearchInfo->_dwCurrResult
== phSearchInfo->_dwMaxResultGot);
phSearchInfo->_dwCurrResult++;
phSearchInfo->_dwMaxResultGot++;
if (phSearchInfo->_dwCurrResult >= phSearchInfo->_cSearchResults) {
//
// Need to allocate more memory for handles
//
phSearchInfo->_pSearchResults = (LDAPMessage **) ReallocADsMem(
(void *) phSearchInfo->_pSearchResults,
sizeof(LDAPMessage *) *
phSearchInfo->_cSearchResults,
sizeof(LDAPMessage *) *
(phSearchInfo->_cSearchResults +
NO_LDAP_RESULT_HANDLES));
if(!phSearchInfo->_pSearchResults) {
hr = E_OUTOFMEMORY;
goto error;
}
phSearchInfo->_cSearchResults += NO_LDAP_RESULT_HANDLES;
}
}
else {
//
// Release and use the same space to store the next result.
//
LdapMsgFree(phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]);
}
// Initialize Result to NULL
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult] = NULL;
if ( phSearchInfo->_SearchPref._fAsynchronous ) {
hr = LdapGetNextPage(
phSearchInfo->_pConnection,
phSearchInfo->_hPagedSearch,
phSearchInfo->_SearchPref._dwPageSize,
(ULONG *) &phSearchInfo->_currMsgId
);
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) {
phSearchInfo->_fLastPage = TRUE;
if (phSearchInfo->_SearchPref._fCacheResults) {
phSearchInfo->_dwCurrResult--;
phSearchInfo->_dwMaxResultGot--;
}
RRETURN(S_ADS_NOMORE_ROWS);
}
BAIL_ON_FAILURE(hr);
//
// Wait for one page worth of results to get back.
//
hr = LdapResult(
phSearchInfo->_pConnection,
phSearchInfo->_currMsgId,
LDAP_MSG_ALL,
phSearchInfo->_SearchPref._timeout.tv_sec ?
&phSearchInfo->_SearchPref._timeout : NULL,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&resType
);
phSearchInfo->_fLastResult = FALSE;
if ((hr == HRESULT_FROM_WIN32(ERROR_DS_INVALID_DN_SYNTAX)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_NAMING_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_OBJ_CLASS_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_FILTER_UNKNOWN)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_PARAM_ERROR)) ||
(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER))) {
phSearchInfo->_fLastResult = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
}
BAIL_ON_FAILURE(hr);
hr = LdapGetPagedCount(
phSearchInfo->_pConnection,
phSearchInfo->_hPagedSearch,
&totalCount,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
);
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) {
hr = S_OK;
}
BAIL_ON_FAILURE(hr);
}
else {
hr = LdapGetNextPageS(
phSearchInfo->_pConnection,
phSearchInfo->_hPagedSearch,
phSearchInfo->_SearchPref._timeout.tv_sec ?
&phSearchInfo->_SearchPref._timeout : NULL,
phSearchInfo->_SearchPref._dwPageSize,
&totalCount,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
);
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_RESULTS_RETURNED)) {
//
// Since we hit the last page we need to get the
// count of the max and currResults down by one.
//
if (phSearchInfo->_SearchPref._fCacheResults) {
phSearchInfo->_dwCurrResult--;
phSearchInfo->_dwMaxResultGot--;
}
phSearchInfo->_fLastPage = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
}
BAIL_ON_FAILURE(hr);
}
RRETURN(S_OK);
}
//
// If we came here, we have reached the end
//
hr = S_ADS_NOMORE_ROWS;
error:
RRETURN(hr);
}
HRESULT
ADsGetPreviousRow(
IN ADS_SEARCH_HANDLE hSearchHandle,
IN CCredentials& Credentials
)
{
PLDAP_SEARCHINFO phSearchInfo = (PLDAP_SEARCHINFO) hSearchHandle;
LDAPMessage *pTmpRow, *pTargetRow, *pPrevRow = NULL;
DWORD dwOrigCurrResult;
HRESULT hr;
if (!phSearchInfo) {
RRETURN(E_ADS_BAD_PARAMETER);
}
if(!phSearchInfo->_pConnection) {
// cannot ask for previous row if connection not established
RRETURN(E_ADS_BAD_PARAMETER);
}
if(!phSearchInfo->_pSearchResults) {
// cannot ask for previous row if no results have been obtained
RRETURN(E_ADS_BAD_PARAMETER);
}
// save value in case we need to restore later
dwOrigCurrResult = phSearchInfo->_dwCurrResult;
if(NULL == phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult])
// we are at the end of the entire search result list
{
ADsAssert(!phSearchInfo->_pCurrentRow);
if(phSearchInfo->_dwCurrResult > 0)
// we need to get the last entry of the previous result
phSearchInfo->_dwCurrResult--;
else
{
phSearchInfo->_fBefFirstRow = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
}
}
ADsAssert(phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]);
hr = LdapFirstEntry(phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&pTmpRow);
BAIL_ON_FAILURE(hr);
// row whose predecessor we are looking for (this may be NULL)
pTargetRow = phSearchInfo->_pCurrentRow;
if(pTmpRow == pTargetRow)
// we are at the first row of the current result
{
if(phSearchInfo->_dwCurrResult > 0)
{
// we need to get the last entry of the previous result
phSearchInfo->_dwCurrResult--;
pTargetRow = NULL;
hr = LdapFirstEntry(phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&pTmpRow);
BAIL_ON_FAILURE(hr);
}
else
{
phSearchInfo->_pCurrentRow = NULL;
phSearchInfo->_fBefFirstRow = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
}
}
while(pTmpRow != pTargetRow)
{
pPrevRow = pTmpRow;
hr = LdapNextEntry(phSearchInfo->_pConnection,
pTmpRow,
&pTmpRow);
BAIL_ON_FAILURE(hr);
}
ADsAssert(pPrevRow);
phSearchInfo->_pCurrentRow = pPrevRow;
phSearchInfo->_pFirstAttr = NULL;
RRETURN(S_OK);
error:
phSearchInfo->_dwCurrResult = dwOrigCurrResult;
RRETURN(hr);
}
HRESULT
ADsGetColumn(
IN ADS_SEARCH_HANDLE hSearchHandle,
IN LPWSTR pszColumnName,
IN CCredentials& Credentials,
DWORD dwPort,
OUT PADS_SEARCH_COLUMN pColumn
)
{
PLDAP_SEARCHINFO phSearchInfo = (PLDAP_SEARCHINFO) hSearchHandle;
VOID **ppValue = NULL;
struct berval **ppBerValue = NULL;
WCHAR **ppStrValue = NULL;
int cValueCount;
HRESULT hr = S_OK;
DWORD dwStatus;
LPWSTR pszDn, pszADsPathName = NULL;
DWORD dwSyntaxId;
DWORD dwError;
WCHAR pszErrorBuf[MAX_PATH], pszNameBuf[MAX_PATH];
PADS_VLV pVLV = NULL;
if( !pColumn ||
!phSearchInfo ||
!phSearchInfo->_pSearchResults )
RRETURN (E_ADS_BAD_PARAMETER);
pColumn->pszAttrName = NULL;
pColumn->dwADsType = ADSTYPE_INVALID;
pColumn->pADsValues = NULL;
pColumn->dwNumValues = 0;
pColumn->hReserved = NULL;
if(!phSearchInfo->_pConnection)
RRETURN (E_ADS_BAD_PARAMETER);
if (!phSearchInfo->_pCurrentRow
&& _wcsicmp(pszColumnName, ADS_DIRSYNC_COOKIE)
&& _wcsicmp(pszColumnName, ADS_VLV_RESPONSE)) {
//
// Current row is not valid and you are not asking for the
// dirsync cookie - so we will fail.
//
RRETURN(E_ADS_BAD_PARAMETER);
}
pColumn->pszAttrName = AllocADsStr(pszColumnName);
if (!pColumn->pszAttrName)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
if(!_wcsicmp (pszColumnName, L"ADsPath")) {
//
// Get the DN of the entry.
//
hr = LdapGetDn(
phSearchInfo->_pConnection,
phSearchInfo->_pCurrentRow,
&pszDn
);
BAIL_ON_FAILURE(hr);
//
// Build the ADsPath
//
hr = BuildADsPathFromLDAPPath(
phSearchInfo->_pszADsPathContext,
pszDn,
&pszADsPathName
);
BAIL_ON_FAILURE(hr);
LdapMemFree(pszDn);
pColumn->pADsValues = (PADSVALUE) AllocADsMem(sizeof(ADSVALUE));
if (!pColumn->pADsValues)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pColumn->dwADsType = ADSTYPE_CASE_IGNORE_STRING;
pColumn->dwNumValues = 1;
pColumn->pADsValues[0].dwType = ADSTYPE_CASE_IGNORE_STRING;
pColumn->pADsValues[0].CaseIgnoreString = AllocADsStr(pszADsPathName);
if (!pColumn->pADsValues[0].CaseIgnoreString)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
FreeADsMem(pszADsPathName);
RRETURN(S_OK);
}
else if (phSearchInfo->_SearchPref._fDirSync) {
//
// See if we need to return the dirsync control info.
//
if (!_wcsicmp (pszColumnName, ADS_DIRSYNC_COOKIE)) {
pColumn->pADsValues = (PADSVALUE) AllocADsMem(sizeof(ADSVALUE));
if (!pColumn->pADsValues)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pColumn->dwADsType = ADSTYPE_PROV_SPECIFIC;
pColumn->dwNumValues = 1;
pColumn->pADsValues[0].dwType = ADSTYPE_PROV_SPECIFIC;
//
// Copy the control over if appropriate if not we will
// return NULL or empty data for the result.
//
if (phSearchInfo->_SearchPref._pProvSpecific->lpValue) {
pColumn->pADsValues[0].ProviderSpecific.dwLength =
phSearchInfo->_SearchPref._pProvSpecific->dwLength;
pColumn->pADsValues[0].ProviderSpecific.lpValue = (LPBYTE)
AllocADsMem(
pColumn->pADsValues[0].ProviderSpecific.dwLength
);
if (!pColumn->pADsValues[0].ProviderSpecific.lpValue) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
memcpy(
pColumn->pADsValues[0].ProviderSpecific.lpValue,
phSearchInfo->_SearchPref._pProvSpecific->lpValue,
phSearchInfo->_SearchPref._pProvSpecific->dwLength
);
}
RRETURN(S_OK);
} // if DirSyncControlStruct is being asked for.
} // if dirsync set.
else if (phSearchInfo->_SearchPref._pVLVInfo) {
//
// See if we need to return the VLV control info.
//
if (!_wcsicmp (pszColumnName, ADS_VLV_RESPONSE)) {
pColumn->pADsValues = (PADSVALUE) AllocADsMem(sizeof(ADSVALUE));
if (!pColumn->pADsValues)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pColumn->dwADsType = ADSTYPE_PROV_SPECIFIC;
pColumn->dwNumValues = 1;
pColumn->pADsValues[0].dwType = ADSTYPE_PROV_SPECIFIC;
pColumn->pADsValues[0].ProviderSpecific.dwLength = sizeof(ADS_VLV);
pColumn->pADsValues[0].ProviderSpecific.lpValue = (LPBYTE) AllocADsMem(sizeof(ADS_VLV));
if (!pColumn->pADsValues[0].ProviderSpecific.lpValue)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
// copy the VLV data into the ADS_VLV
pVLV = (PADS_VLV) pColumn->pADsValues[0].ProviderSpecific.lpValue;
pVLV->dwBeforeCount = 0;
pVLV->dwAfterCount = 0;
pVLV->dwOffset = phSearchInfo->_dwVLVOffset;
pVLV->dwContentCount = phSearchInfo->_dwVLVCount;
pVLV->pszTarget = NULL;
// copy the VLV context ID, if available
pVLV->lpContextID = NULL;
pVLV->dwContextIDLength = 0;
if (phSearchInfo->_pVLVContextID && phSearchInfo->_pVLVContextID->bv_len) {
pVLV->lpContextID = (LPBYTE) AllocADsMem(phSearchInfo->_pVLVContextID->bv_len);
if (!pVLV->lpContextID)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pVLV->dwContextIDLength = phSearchInfo->_pVLVContextID->bv_len;
memcpy(pVLV->lpContextID,
phSearchInfo->_pVLVContextID->bv_val,
phSearchInfo->_pVLVContextID->bv_len);
}
RRETURN(S_OK);
} // if VLV response is being asked for
} // if VLV set
if (phSearchInfo->_fADsPathOnly) {
//
// Only ADsPath attribute requested in the search,
// so don't return any other column values.
//
RRETURN (E_ADS_COLUMN_NOT_SET);
}
if (phSearchInfo->_SearchPref._fAttrsOnly) {
//
// Only Names got. So, don't return any values
//
RRETURN (S_OK);
}
//
// Call the helper function to get the LDAP specific type
//
hr = LdapGetSyntaxOfAttributeOnServer(
phSearchInfo->_pszLdapServer,
pszColumnName,
&dwSyntaxId,
Credentials,
dwPort
);
if (hr == E_ADS_CANT_CONVERT_DATATYPE) {
//
// This means that the server didn't give back the schema and we don't
// have it in the default schema. Return an octet blob.
//
dwSyntaxId = LDAPTYPE_OCTETSTRING;
hr = S_OK;
}
if (hr == E_ADS_PROPERTY_NOT_FOUND) {
//
// Not on the server, we will return as provider specific.
// LDAPTYPE_UNKNOWN will be mapped to ADSTYPE_PROVIDER_SPECIFIC
// when we build the ADsColumn.
//
dwSyntaxId = LDAPTYPE_UNKNOWN;
hr = S_OK;
}
BAIL_ON_FAILURE(hr);
//
// Now get the data
//
switch ( dwSyntaxId )
{
case LDAPTYPE_CERTIFICATE:
case LDAPTYPE_CERTIFICATELIST:
case LDAPTYPE_CERTIFICATEPAIR:
case LDAPTYPE_PASSWORD:
case LDAPTYPE_TELETEXTERMINALIDENTIFIER:
case LDAPTYPE_AUDIO:
case LDAPTYPE_JPEG:
case LDAPTYPE_FAX:
case LDAPTYPE_OCTETSTRING:
case LDAPTYPE_SECURITY_DESCRIPTOR:
case LDAPTYPE_UNKNOWN:
hr = LdapGetValuesLen(
phSearchInfo->_pConnection,
phSearchInfo->_pCurrentRow,
pszColumnName,
&ppBerValue,
&cValueCount
);
ppValue = (VOID **) ppBerValue;
break;
default:
hr = LdapGetValues(
phSearchInfo->_pConnection,
phSearchInfo->_pCurrentRow,
pszColumnName,
&ppStrValue,
&cValueCount
);
ppValue = (VOID **) ppStrValue;
break;
}
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE)) {
hr=E_ADS_COLUMN_NOT_SET;
}
BAIL_ON_FAILURE(hr);
hr = LdapValueToADsColumn(
pszColumnName,
dwSyntaxId,
cValueCount,
ppValue,
pColumn
);
BAIL_ON_FAILURE(hr);
RRETURN(S_OK);
error:
ADsFreeColumn(pColumn);
if (pszADsPathName)
FreeADsMem(pszADsPathName);
RRETURN (hr);
}
HRESULT
ADsGetNextColumnName(
IN ADS_SEARCH_HANDLE hSearchHandle,
OUT LPWSTR * ppszColumnName
)
{
HRESULT hr = S_OK;
PLDAP_SEARCHINFO phSearchInfo = (PLDAP_SEARCHINFO) hSearchHandle;
DWORD dwStatus, dwError;
WCHAR pszErrorBuf[MAX_PATH], pszNameBuf[MAX_PATH];
if( !phSearchInfo ||
!phSearchInfo->_pSearchResults ||
!ppszColumnName)
RRETURN (E_ADS_BAD_PARAMETER);
*ppszColumnName = NULL;
if (!phSearchInfo->_fADsPathOnly) {
if (!phSearchInfo->_pFirstAttr)
hr = LdapFirstAttribute(
phSearchInfo->_pConnection,
phSearchInfo->_pCurrentRow,
&phSearchInfo->_pFirstAttr,
ppszColumnName
);
else
hr = LdapNextAttribute(
phSearchInfo->_pConnection,
phSearchInfo->_pCurrentRow,
phSearchInfo->_pFirstAttr,
ppszColumnName
);
BAIL_ON_FAILURE(hr);
}
if (*ppszColumnName) {
// Nothing to do in this case.
}
else if ( phSearchInfo->_fADsPathPresent) {
//
// If ADsPath was specified return it as the last column
//
if (!phSearchInfo->_fADsPathReturned) {
*ppszColumnName = AllocADsStr(L"ADsPath");
phSearchInfo->_fADsPathReturned = TRUE;
}
else {
//
// We need to reset it back so that we return it for the next
// row
//
phSearchInfo->_fADsPathReturned = FALSE;
hr = S_ADS_NOMORE_COLUMNS;
}
}
else {
hr = S_ADS_NOMORE_COLUMNS;
}
error:
RRETURN (hr);
}
HRESULT
ADsFreeColumn(
IN PADS_SEARCH_COLUMN pColumn
)
{
HRESULT hr = S_OK;
if(!pColumn)
RRETURN (E_ADS_BAD_PARAMETER);
switch(pColumn->dwADsType) {
case ADSTYPE_OCTET_STRING:
case ADSTYPE_NT_SECURITY_DESCRIPTOR:
case ADSTYPE_PROV_SPECIFIC:
//
// Call the LDAP free value routine if not DirSyncControl
// or VLV
//
if (pColumn->pszAttrName
&& !_wcsicmp(ADS_VLV_RESPONSE, pColumn->pszAttrName)) {
//
// VLV, so free the ADS_VLV and its members
//
if (pColumn->pADsValues && pColumn->pADsValues[0].ProviderSpecific.lpValue) {
if (((PADS_VLV)(pColumn->pADsValues[0].ProviderSpecific.lpValue))->lpContextID) {
FreeADsMem(((PADS_VLV)(pColumn->pADsValues[0].ProviderSpecific.lpValue))->lpContextID);
}
FreeADsMem(pColumn->pADsValues[0].ProviderSpecific.lpValue);
}
}
else if (pColumn->pszAttrName
&& _wcsicmp(ADS_DIRSYNC_COOKIE, pColumn->pszAttrName)
) {
LdapValueFreeLen((struct berval **)pColumn->hReserved);
pColumn->hReserved = NULL;
} else {
//
// DirSyncControlStruct - so we free the ADsValue.
//
if (pColumn->pADsValues[0].ProviderSpecific.lpValue) {
FreeADsMem(pColumn->pADsValues[0].ProviderSpecific.lpValue);
}
}
break;
case ADSTYPE_CASE_IGNORE_STRING:
case ADSTYPE_NUMERIC_STRING:
case ADSTYPE_PRINTABLE_STRING:
case ADSTYPE_DN_STRING:
case ADSTYPE_CASE_EXACT_STRING:
if(!pColumn->hReserved) {
//
// The column just contains a DN.
//
FreeADsMem(pColumn->pADsValues[0].CaseIgnoreString);
}
else {
LdapValueFree( (WCHAR **)pColumn->hReserved);
pColumn->hReserved = NULL;
}
break;
case ADSTYPE_INTEGER:
case ADSTYPE_LARGE_INTEGER:
case ADSTYPE_BOOLEAN:
case ADSTYPE_UTC_TIME:
// Nothing to free
break;
case ADSTYPE_DN_WITH_BINARY:
case ADSTYPE_DN_WITH_STRING:
AdsTypeFreeAdsObjects(
pColumn->pADsValues,
pColumn->dwNumValues
);
//
// Do not want to free this twice
//
pColumn->pADsValues = NULL;
break;
case ADSTYPE_INVALID:
//
// This comes from the result of search by setting _SearchPref._fAttrsOnly
// nothing need to be done
//
break;
default:
// unknown type;
hr = E_ADS_BAD_PARAMETER;
}
if (pColumn->pszAttrName)
FreeADsStr(pColumn->pszAttrName);
if (pColumn->pADsValues) {
FreeADsMem(pColumn->pADsValues);
pColumn->pADsValues = NULL;
}
RRETURN(hr);
}
BOOL
IsValidPrefValue(
ADS_SEARCHPREF_INFO SearchPref
)
{
switch(SearchPref.dwSearchPref) {
case ADS_SEARCHPREF_ASYNCHRONOUS:
case ADS_SEARCHPREF_ATTRIBTYPES_ONLY:
case ADS_SEARCHPREF_CACHE_RESULTS:
case ADS_SEARCHPREF_TOMBSTONE:
if (SearchPref.vValue.dwType != ADSTYPE_BOOLEAN)
return FALSE;
break;
case ADS_SEARCHPREF_DEREF_ALIASES:
case ADS_SEARCHPREF_SIZE_LIMIT:
case ADS_SEARCHPREF_TIME_LIMIT:
case ADS_SEARCHPREF_SEARCH_SCOPE:
case ADS_SEARCHPREF_TIMEOUT:
case ADS_SEARCHPREF_PAGESIZE:
case ADS_SEARCHPREF_PAGED_TIME_LIMIT:
case ADS_SEARCHPREF_CHASE_REFERRALS:
if (SearchPref.vValue.dwType != ADSTYPE_INTEGER)
return FALSE;
break;
case ADS_SEARCHPREF_SORT_ON:
if (SearchPref.vValue.dwType != ADSTYPE_PROV_SPECIFIC)
return FALSE;
break;
case ADS_SEARCHPREF_DIRSYNC:
if (SearchPref.vValue.dwType != ADSTYPE_PROV_SPECIFIC)
return FALSE;
break;
case ADS_SEARCHPREF_VLV:
if (SearchPref.vValue.dwType != ADSTYPE_PROV_SPECIFIC)
return FALSE;
break;
case ADS_SEARCHPREF_ATTRIBUTE_QUERY:
if (SearchPref.vValue.dwType != ADSTYPE_CASE_IGNORE_STRING)
return FALSE;
break;
case ADS_SEARCHPREF_SECURITY_MASK:
if (SearchPref.vValue.dwType != ADSTYPE_INTEGER)
return FALSE;
break;
default:
return FALSE;
}
return TRUE;
}
HRESULT
LdapValueToADsColumn(
LPWSTR pszColumnName,
DWORD dwSyntaxId,
DWORD dwValues,
VOID **ppValue,
ADS_SEARCH_COLUMN * pColumn
)
{
HRESULT hr = S_OK;
DWORD i, j;
if(!pszColumnName || !pColumn)
RRETURN(E_ADS_BAD_PARAMETER);
pColumn->hReserved = (HANDLE) ppValue;
pColumn->dwNumValues = dwValues;
if (dwValues < 1) {
//
// Need to set the ADsValue struct to NULL as it does
// not make sense to return any ADsValues
//
pColumn->pADsValues = NULL;
} else {
pColumn->pADsValues = (PADSVALUE) AllocADsMem(
sizeof(ADSVALUE) * dwValues
);
if (!pColumn->pADsValues)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pColumn->dwADsType = MapLDAPTypeToADSType(dwSyntaxId);
switch (dwSyntaxId) {
case LDAPTYPE_BITSTRING:
case LDAPTYPE_PRINTABLESTRING:
case LDAPTYPE_DIRECTORYSTRING:
case LDAPTYPE_COUNTRYSTRING:
case LDAPTYPE_DN:
case LDAPTYPE_NUMERICSTRING:
case LDAPTYPE_IA5STRING:
case LDAPTYPE_CASEIGNORESTRING:
case LDAPTYPE_OID:
case LDAPTYPE_TELEPHONENUMBER:
case LDAPTYPE_ATTRIBUTETYPEDESCRIPTION:
case LDAPTYPE_OBJECTCLASSDESCRIPTION:
case LDAPTYPE_DELIVERYMETHOD:
case LDAPTYPE_ENHANCEDGUIDE:
case LDAPTYPE_FACSIMILETELEPHONENUMBER:
case LDAPTYPE_GUIDE:
case LDAPTYPE_NAMEANDOPTIONALUID:
case LDAPTYPE_POSTALADDRESS:
case LDAPTYPE_PRESENTATIONADDRESS:
case LDAPTYPE_TELEXNUMBER:
case LDAPTYPE_DSAQUALITYSYNTAX:
case LDAPTYPE_DATAQUALITYSYNTAX:
case LDAPTYPE_MAILPREFERENCE:
case LDAPTYPE_OTHERMAILBOX:
case LDAPTYPE_ACCESSPOINTDN:
case LDAPTYPE_ORNAME:
case LDAPTYPE_ORADDRESS:
for (i=0; i < dwValues; i++) {
pColumn->pADsValues[i].dwType = pColumn->dwADsType;
pColumn->pADsValues[i].CaseIgnoreString = (LPWSTR) ppValue[i];
}
break;
case LDAPTYPE_CASEEXACTSTRING:
for (i=0; i < dwValues; i++) {
pColumn->pADsValues[i].dwType = pColumn->dwADsType;
pColumn->pADsValues[i].CaseExactString = (LPWSTR) ppValue[i];
}
break;
case LDAPTYPE_UTCTIME:
for (i=0; i < dwValues; i++) {
SYSTEMTIME st;
hr = UTCTimeStringToUTCTime((LPWSTR)ppValue[i],
&st);
BAIL_ON_FAILURE(hr);
pColumn->pADsValues[i].dwType = pColumn->dwADsType;
pColumn->pADsValues[i].UTCTime = st;
}
LdapValueFree((WCHAR **)pColumn->hReserved);
pColumn->hReserved = NULL;
break;
case LDAPTYPE_GENERALIZEDTIME:
for (i=0; i < dwValues; i++) {
SYSTEMTIME st;
hr = GenTimeStringToUTCTime((LPWSTR)ppValue[i],
&st);
BAIL_ON_FAILURE(hr);
pColumn->pADsValues[i].dwType = pColumn->dwADsType;
pColumn->pADsValues[i].UTCTime = st;
}
LdapValueFree((WCHAR **)pColumn->hReserved);
pColumn->hReserved = NULL;
break;
case LDAPTYPE_CERTIFICATE:
case LDAPTYPE_CERTIFICATELIST:
case LDAPTYPE_CERTIFICATEPAIR:
case LDAPTYPE_PASSWORD:
case LDAPTYPE_TELETEXTERMINALIDENTIFIER:
case LDAPTYPE_AUDIO:
case LDAPTYPE_JPEG:
case LDAPTYPE_FAX:
case LDAPTYPE_OCTETSTRING:
case LDAPTYPE_SECURITY_DESCRIPTOR:
for (i=0; i < dwValues; i++) {
pColumn->pADsValues[i].dwType = pColumn->dwADsType;
pColumn->pADsValues[i].OctetString.dwLength = ((struct berval **)ppValue)[i]->bv_len;
pColumn->pADsValues[i].OctetString.lpValue = (LPBYTE)
((struct berval **) ppValue)[i]->bv_val;
}
break;
case LDAPTYPE_BOOLEAN:
for (i=0; i < dwValues; i++) {
pColumn->pADsValues[i].dwType = pColumn->dwADsType;
if ( _wcsicmp( (WCHAR *) ppValue[i], L"TRUE") == 0 ) {
pColumn->pADsValues[i].Boolean = TRUE;
}
else if ( _wcsicmp( (WCHAR *) ppValue[i], L"FALSE") == 0 ) {
pColumn->pADsValues[i].Boolean = FALSE;
}
else {
BAIL_ON_FAILURE(hr = E_ADS_CANT_CONVERT_DATATYPE);
}
}
LdapValueFree((WCHAR **)pColumn->hReserved);
pColumn->hReserved = NULL;
break;
case LDAPTYPE_INTEGER:
for (i=0; i < dwValues; i++) {
pColumn->pADsValues[i].dwType = pColumn->dwADsType;
pColumn->pADsValues[i].Integer = _wtol((WCHAR *) ppValue[i]);
}
LdapValueFree((WCHAR **)pColumn->hReserved);
pColumn->hReserved = NULL;
break;
case LDAPTYPE_INTEGER8:
for (i=0; i < dwValues; i++) {
pColumn->pADsValues[i].dwType = pColumn->dwADsType;
swscanf ((WCHAR *) ppValue[i], L"%I64d", &pColumn->pADsValues[i].LargeInteger);
}
LdapValueFree((WCHAR **)pColumn->hReserved);
pColumn->hReserved = NULL;
break;
case LDAPTYPE_DNWITHBINARY:
for (i=0; i < dwValues; i++) {
hr = LdapDNWithBinToAdsTypeHelper(
(LPWSTR) ppValue[i],
&pColumn->pADsValues[i]
);
BAIL_ON_FAILURE(hr);
}
LdapValueFree((WCHAR **)pColumn->hReserved);
pColumn->hReserved = NULL;
break;
case LDAPTYPE_DNWITHSTRING:
for (i=0; i < dwValues; i++) {
hr = LdapDNWithStrToAdsTypeHelper(
(LPWSTR) ppValue[i],
&pColumn->pADsValues[i]
);
BAIL_ON_FAILURE(hr);
}
LdapValueFree((WCHAR **)pColumn->hReserved);
pColumn->hReserved = NULL;
break;
default:
pColumn->dwADsType = ADSTYPE_PROV_SPECIFIC;
for (i=0; i < dwValues; i++) {
pColumn->pADsValues[i].dwType = ADSTYPE_PROV_SPECIFIC;
pColumn->pADsValues[i].ProviderSpecific.dwLength =
((struct berval **)ppValue)[i]->bv_len;
pColumn->pADsValues[i].ProviderSpecific.lpValue =
(LPBYTE) ((struct berval **) ppValue)[i]->bv_val;
}
break;
}
RRETURN(hr);
error:
if (pColumn->pADsValues) {
FreeADsMem(pColumn->pADsValues);
pColumn->pADsValues = NULL;
pColumn->dwNumValues = 0;
}
RRETURN(hr);
}
//
// To add the server controls. The controls will be set internally in the
// handle. Right now, we support sort, dirsync and domain scope controls.
//
HRESULT
AddSearchControls(
PLDAP_SEARCHINFO phSearchInfo,
CCredentials& Credentials
)
{
HRESULT hr = S_OK;
PLDAPSortKey *ppSortKeys = NULL;
PLDAPControl pSortControl = NULL, *ppServerControls = NULL;
PLDAPControl pDirSyncControl = NULL;
PLDAPControl pDomCtrl = NULL;
PLDAPControl pTombStoneCtrl = NULL;
PLDAPControl pVLVControl = NULL;
PLDAPControl pAttribScopedCtrl = NULL;
PLDAPControl pSecurityDescCtrl = NULL;
PBERVAL pBerVal = NULL;
DWORD nKeys=0, i=0;
DWORD dwControls = 0;
DWORD dwCurControl = 0;
BOOL fDomainScopeControl = FALSE;
BOOL fTombStone = FALSE;
BYTE * pbSecDescValue = NULL;
if (phSearchInfo->_SearchPref._pSortKeys) {
dwControls++;
}
if (phSearchInfo->_SearchPref._fDirSync) {
dwControls++;
}
if (phSearchInfo->_SearchPref._fTombStone) {
dwControls++;
fTombStone = TRUE;
}
if (phSearchInfo->_SearchPref._pVLVInfo) {
dwControls++;
}
if (phSearchInfo->_SearchPref._pAttribScoped) {
dwControls++;
}
if (phSearchInfo->_SearchPref._fSecurityDescriptorControl) {
dwControls++;
}
if (phSearchInfo->_SearchPref._dwChaseReferrals == LDAP_CHASE_EXTERNAL_REFERRALS
|| phSearchInfo->_SearchPref._dwChaseReferrals == (DWORD)(DWORD_PTR)LDAP_OPT_OFF) {
//
// Try and see if we can add the additional ADControl.
//
hr = ReadDomScopeSupportedAttr(
phSearchInfo->_pszLdapServer,
&fDomainScopeControl,
Credentials,
phSearchInfo->_dwPort
);
if (FAILED(hr)) {
hr = S_OK;
fDomainScopeControl = FALSE;
}
else if (fDomainScopeControl == TRUE) {
dwControls++;
}
}
if (!dwControls) {
RRETURN(S_OK);
}
ADsAssert(phSearchInfo);
if (phSearchInfo->_ServerControls) {
while (phSearchInfo->_ServerControls[i]) {
//
// Free the pre-existing controls in preparation for adding in a new
// batch.
//
// The algorithm is:
// If this is the VLV control, free it with LdapControlFree
// All other controls are freed with FreeADsMem
// The sort & security descriptor controls also have additional
// memory associated with them that must be freed here.
// (some other controls, like ASQ or DirSync, also have additonal
// memory that must be freed, but this memory is tracked via
// _ldap_searchinfo and the freeing is done when we actually
// process adding the new control below)
//
//
// If this is the VLV control, need to free it
// using LdapControlFree
//
if ((phSearchInfo->_ServerControls[i]->ldctl_oid)
&& (wcscmp(
phSearchInfo->_ServerControls[i]->ldctl_oid,
LDAP_CONTROL_VLVREQUEST_W
) == 0)) {
LdapControlFree(phSearchInfo->_ServerControls[i]);
}
else {
//
// If this is the sort or security descriptor control, we
// need to free some additional stuff.
//
if ((phSearchInfo->_ServerControls[i]->ldctl_oid)
&& (wcscmp(
phSearchInfo->_ServerControls[i]->ldctl_oid,
LDAP_SERVER_SORT_OID_W
) == 0)
) {
//
// This is a sort control
//
if (phSearchInfo->_ServerControls[i]->ldctl_oid) {
ldap_memfree(phSearchInfo->_ServerControls[i]->ldctl_oid);
}
if (phSearchInfo->_ServerControls[i]->ldctl_value.bv_val) {
ldap_memfreeA(
phSearchInfo->_ServerControls[i]->ldctl_value.bv_val
);
}
}
else if ((phSearchInfo->_ServerControls[i]->ldctl_oid)
&& (wcscmp(phSearchInfo->_ServerControls[i]->ldctl_oid,
LDAP_SERVER_SD_FLAGS_OID_W) == 0)) {
//
// This is a security descriptor control
//
if (phSearchInfo->_ServerControls[i]->ldctl_value.bv_val) {
FreeADsMem(phSearchInfo->_ServerControls[i]->ldctl_value.bv_val);
}
}
// free the control (for any control except VLV, which
// we already freed above)
FreeADsMem(phSearchInfo->_ServerControls[i]);
}
i++;
}
FreeADsMem(phSearchInfo->_ServerControls);
phSearchInfo->_ServerControls = NULL;
}
nKeys = phSearchInfo->_SearchPref._nSortKeys;
//
// One more than our dwControls is the number we need.
//
ppServerControls = (PLDAPControl *)
AllocADsMem( sizeof(PLDAPControl) * (dwControls+1) );
if (!ppServerControls) {
RRETURN(E_OUTOFMEMORY);
}
//
// Process the VLV control
//
if (phSearchInfo->_SearchPref._pVLVInfo) {
hr = LdapCreateVLVControl(phSearchInfo->_pConnection,
phSearchInfo->_SearchPref._pVLVInfo,
TRUE,
&pVLVControl
);
BAIL_ON_FAILURE(hr);
ppServerControls[dwCurControl++] = pVLVControl;
}
//
// Process the sort control.
//
if (phSearchInfo->_SearchPref._pSortKeys) {
ppSortKeys = (PLDAPSortKey *) AllocADsMem( sizeof(PLDAPSortKey) *
(nKeys+1) );
if (!ppSortKeys) {
RRETURN(E_OUTOFMEMORY);
}
pSortControl = (LDAPControl *) AllocADsMem(sizeof(LDAPControl));
if (!pSortControl) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
for (i=0; i<nKeys; i++) {
ppSortKeys[i] = &(phSearchInfo->_SearchPref._pSortKeys[i]);
}
ppSortKeys[nKeys] = NULL;
hr = LdapEncodeSortControl(
phSearchInfo->_pConnection,
ppSortKeys,
pSortControl,
TRUE
);
BAIL_ON_FAILURE(hr);
ppServerControls[dwCurControl++] = pSortControl;
if (ppSortKeys) {
FreeADsMem(ppSortKeys);
}
}
//
// Handle the dirsync control if applicable
//
if (phSearchInfo->_SearchPref._fDirSync) {
pDirSyncControl = (LDAPControl *) AllocADsMem(sizeof(LDAPControl));
if (!pDirSyncControl) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
hr = BerEncodeReplicationCookie(
phSearchInfo->_SearchPref._pProvSpecific->lpValue,
phSearchInfo->_SearchPref._pProvSpecific->dwLength,
&pBerVal
);
BAIL_ON_FAILURE(hr);
pDirSyncControl->ldctl_oid = LDAP_SERVER_DIRSYNC_OID_W;
pDirSyncControl->ldctl_value.bv_len = pBerVal->bv_len;
pDirSyncControl->ldctl_value.bv_val = (PCHAR) pBerVal->bv_val;
pDirSyncControl->ldctl_iscritical = TRUE;
//
// Clear the info in the search handle if applicable
//
if (phSearchInfo->_pBerVal) {
ber_bvfree(phSearchInfo->_pBerVal);
}
phSearchInfo->_pBerVal = pBerVal;
ppServerControls[dwCurControl++] = pDirSyncControl;
}
//
// Process the DomainScope control if applicable
//
if (fDomainScopeControl) {
pDomCtrl = (LDAPControl *) AllocADsMem(sizeof(LDAPControl));
if (!pDomCtrl) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pDomCtrl->ldctl_oid = LDAP_SERVER_DOMAIN_SCOPE_OID_W;
pDomCtrl->ldctl_value.bv_len = 0;
pDomCtrl->ldctl_value.bv_val = NULL;
pDomCtrl->ldctl_iscritical = FALSE;
ppServerControls[dwCurControl++] = pDomCtrl;
}
//
// Process the tombstone control if applicable
//
if (fTombStone) {
pTombStoneCtrl = (LDAPControl *) AllocADsMem(sizeof(LDAPControl));
if (!pTombStoneCtrl) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pTombStoneCtrl->ldctl_oid = LDAP_SERVER_SHOW_DELETED_OID_W;
pTombStoneCtrl->ldctl_value.bv_len = 0;
pTombStoneCtrl->ldctl_value.bv_val = NULL;
pTombStoneCtrl->ldctl_iscritical = TRUE;
ppServerControls[dwCurControl++] = pTombStoneCtrl;
}
//
// Process the attribute scoped query control
//
if (phSearchInfo->_SearchPref._pAttribScoped) {
pAttribScopedCtrl = (LDAPControl *) AllocADsMem(sizeof(LDAPControl));
if (!pAttribScopedCtrl) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
hr = BerEncodeAttribScopedControlValue(phSearchInfo->_SearchPref._pAttribScoped,
&pBerVal);
BAIL_ON_FAILURE(hr);
pAttribScopedCtrl->ldctl_oid = LDAP_SERVER_ASQ_OID_W;
pAttribScopedCtrl->ldctl_value.bv_len = pBerVal->bv_len;
pAttribScopedCtrl->ldctl_value.bv_val = pBerVal->bv_val;
pAttribScopedCtrl->ldctl_iscritical = TRUE;
//
// Clear the info in the search handle if applicable
//
if (phSearchInfo->_pBerValAttribScoped) {
ber_bvfree(phSearchInfo->_pBerValAttribScoped);
}
phSearchInfo->_pBerValAttribScoped = pBerVal;
ppServerControls[dwCurControl++] = pAttribScopedCtrl;
}
//
// Process the security descriptor control
//
if (phSearchInfo->_SearchPref._fSecurityDescriptorControl) {
pSecurityDescCtrl = (LDAPControl *) AllocADsMem(sizeof(LDAPControl));
if (!pSecurityDescCtrl) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pbSecDescValue = (BYTE *) AllocADsMem(5);
if (!pbSecDescValue) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
ZeroMemory(pbSecDescValue, 5);
pbSecDescValue[0] = 0x30; // Start sequence tag
pbSecDescValue[1] = 0x03; // Length in bytes of following
pbSecDescValue[2] = 0x02; // Actual value this and next 2
pbSecDescValue[3] = 0x01;
pbSecDescValue[4] = (BYTE) ((ULONG)phSearchInfo->_SearchPref._SecurityDescriptorMask);
pSecurityDescCtrl->ldctl_oid = LDAP_SERVER_SD_FLAGS_OID_W;
pSecurityDescCtrl->ldctl_value.bv_len = 5;
pSecurityDescCtrl->ldctl_value.bv_val = (PCHAR) pbSecDescValue;
pSecurityDescCtrl->ldctl_iscritical = TRUE;
ppServerControls[dwCurControl++] = pSecurityDescCtrl;
}
ppServerControls[dwControls] = NULL;
phSearchInfo->_ServerControls = ppServerControls;
RRETURN(S_OK);
error:
if (ppServerControls) {
FreeADsMem(ppServerControls);
}
if (pSortControl) {
FreeADsMem(pSortControl);
}
if (pDirSyncControl) {
FreeADsMem(pSortControl);
}
if (pDomCtrl) {
FreeADsMem(pDomCtrl);
}
if (pTombStoneCtrl) {
FreeADsMem(pTombStoneCtrl);
}
if (pAttribScopedCtrl) {
FreeADsMem(pAttribScopedCtrl);
}
if (pVLVControl) {
LdapControlFree(pVLVControl);
}
if (pSecurityDescCtrl) {
FreeADsMem(pSecurityDescCtrl);
}
if (ppSortKeys) {
FreeADsMem(ppSortKeys);
}
if (pbSecDescValue) {
FreeADsMem(pbSecDescValue);
}
RRETURN(hr);
}
void
FreeSortKeys(
IN PLDAPSortKey pSortKeys,
IN DWORD dwSortKeys
)
{
for (DWORD i=0; i < dwSortKeys; i++) {
if (pSortKeys[i].sk_attrtype) {
FreeADsStr(pSortKeys[i].sk_attrtype);
}
}
if (pSortKeys) {
FreeADsMem(pSortKeys);
}
}
//
// Copy a LDAPVLVInfo (and the data it points to) from
// *pVLVInfoSource to **ppVLVInfoTarget.
//
// Note that pVLVInfoSource->ldvlv_extradata is not copied,
// and is set to NULL in **ppVLVInfoTarget. If the caller
// uses this for anything, copying it is the caller's
// responsibility.
//
HRESULT
CopyLDAPVLVInfo(
PLDAPVLVInfo pVLVInfoSource,
PLDAPVLVInfo *ppVLVInfoTarget
)
{
HRESULT hr = S_OK;
PLDAPVLVInfo pVLVInfo = NULL;
if (!pVLVInfoSource || !ppVLVInfoTarget)
BAIL_ON_FAILURE(hr = E_INVALIDARG);
*ppVLVInfoTarget = NULL;
pVLVInfo = (PLDAPVLVInfo) AllocADsMem(sizeof(LDAPVLVInfo));
if (!pVLVInfo)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
// copy the non-pointer members
*pVLVInfo = *pVLVInfoSource;
pVLVInfo->ldvlv_attrvalue = NULL;
pVLVInfo->ldvlv_context = NULL;
pVLVInfo->ldvlv_extradata = NULL;
// copy the pointer members
if (pVLVInfoSource->ldvlv_attrvalue) {
pVLVInfo->ldvlv_attrvalue = (PBERVAL) AllocADsMem(sizeof(BERVAL));
if (!pVLVInfo->ldvlv_attrvalue)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pVLVInfo->ldvlv_attrvalue->bv_len = pVLVInfoSource->ldvlv_attrvalue->bv_len;
pVLVInfo->ldvlv_attrvalue->bv_val = (PCHAR) AllocADsMem(pVLVInfo->ldvlv_attrvalue->bv_len);
if (!pVLVInfo->ldvlv_attrvalue->bv_val)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
memcpy(pVLVInfo->ldvlv_attrvalue->bv_val,
pVLVInfoSource->ldvlv_attrvalue->bv_val,
pVLVInfo->ldvlv_attrvalue->bv_len);
}
if (pVLVInfoSource->ldvlv_context) {
pVLVInfo->ldvlv_context = (PBERVAL) AllocADsMem(sizeof(BERVAL));
if (!pVLVInfo->ldvlv_context)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pVLVInfo->ldvlv_context->bv_len = pVLVInfoSource->ldvlv_context->bv_len;
pVLVInfo->ldvlv_context->bv_val = (PCHAR) AllocADsMem(pVLVInfo->ldvlv_context->bv_len);
if (!pVLVInfo->ldvlv_context->bv_val)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
memcpy(pVLVInfo->ldvlv_context->bv_val,
pVLVInfoSource->ldvlv_context->bv_val,
pVLVInfo->ldvlv_context->bv_len);
}
*ppVLVInfoTarget = pVLVInfo;
RRETURN(hr);
error:
FreeLDAPVLVInfo(pVLVInfo);
RRETURN(hr);
}
//
// Free a LDAPVLVInfo (and the data it points to)
//
// Note that pVLVInfoSource->ldvlv_extradata is not freed.
// If the caller uses this for anything, freeing it before
// calling this function is the caller's responsibility.
//
void
FreeLDAPVLVInfo(
IN PLDAPVLVInfo pVLVInfo
)
{
if (pVLVInfo) {
if (pVLVInfo->ldvlv_attrvalue) {
if (pVLVInfo->ldvlv_attrvalue->bv_val) {
FreeADsMem(pVLVInfo->ldvlv_attrvalue->bv_val);
}
FreeADsMem(pVLVInfo->ldvlv_attrvalue);
}
if (pVLVInfo->ldvlv_context) {
if (pVLVInfo->ldvlv_context->bv_val) {
FreeADsMem(pVLVInfo->ldvlv_context->bv_val);
}
FreeADsMem(pVLVInfo->ldvlv_context);
}
FreeADsMem(pVLVInfo);
}
}
HRESULT
StoreVLVInfo(
LDAPMessage *pLDAPMsg,
PLDAP_SEARCHINFO phSearchInfo
)
{
HRESULT hr = S_OK;
PLDAPControl *ppServerControls = NULL;
ULONG ulTarget = 0;
ULONG ulCount = 0;
PBERVAL pContextID = NULL;
PBERVAL pContextIDCopy = NULL;
if (!pLDAPMsg) {
RRETURN(S_OK);
}
//
// Retrieve the server controls
//
hr = LdapParseResult(
phSearchInfo->_pConnection,
pLDAPMsg,
NULL, // ret code
NULL, // matched dn's
NULL, // err msg's
NULL, // referrals
&ppServerControls,
FALSE // freeIt
);
BAIL_ON_FAILURE(hr);
if (!ppServerControls) {
//
// Could not get the control
//
BAIL_ON_FAILURE(hr = E_ADS_PROPERTY_NOT_FOUND);
}
//
// Parse the VLV response control
//
hr = LdapParseVLVControl(
phSearchInfo->_pConnection,
ppServerControls,
&ulTarget,
&ulCount,
&pContextID
);
BAIL_ON_FAILURE(hr);
//
// Copy the new context ID, if one was returned by the server
//
if (pContextID && pContextID->bv_val && pContextID->bv_len) {
pContextIDCopy = (PBERVAL) AllocADsMem(sizeof(BERVAL));
if (!pContextIDCopy)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
pContextIDCopy->bv_len = pContextID->bv_len;
pContextIDCopy->bv_val = (PCHAR) AllocADsMem(pContextID->bv_len);
if (!pContextIDCopy->bv_val)
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
memcpy(pContextIDCopy->bv_val,
pContextID->bv_val,
pContextID->bv_len);
}
//
// Copy VLV response control info into the _ldap_searchinfo
// If the server did not return context ID, pContextIDCopy == NULL.
//
phSearchInfo->_dwVLVOffset = ulTarget;
phSearchInfo->_dwVLVCount = ulCount;
// free the previous context ID
if (phSearchInfo->_pVLVContextID) {
if (phSearchInfo->_pVLVContextID->bv_val) {
FreeADsMem(phSearchInfo->_pVLVContextID->bv_val);
}
FreeADsMem(phSearchInfo->_pVLVContextID);
}
phSearchInfo->_pVLVContextID = pContextIDCopy;
error :
if (pContextID)
BerBvFree(pContextID);
if (ppServerControls) {
ldap_controls_free(ppServerControls);
}
if (FAILED(hr)) {
if (pContextIDCopy) {
if (pContextIDCopy->bv_val) {
FreeADsMem(pContextIDCopy->bv_val);
}
FreeADsMem(pContextIDCopy);
}
}
RRETURN(hr);
}
HRESULT
StoreAttribScopedInfo(
LDAPMessage *pLDAPMsg,
PLDAP_SEARCHINFO phSearchInfo
)
{
HRESULT hr = S_OK;
PLDAPControl *ppServerControls = NULL;
DWORD dwCtr = 0;
BERVAL berVal;
BerElement *pBer = NULL;
int retval = LDAP_SUCCESS;
if (!pLDAPMsg) {
RRETURN(S_OK);
}
hr = LdapParseResult(
phSearchInfo->_pConnection,
pLDAPMsg,
NULL, // ret code
NULL, // matched dn's
NULL, // err msg's
NULL, // referrals
&ppServerControls,
FALSE // freeIt
);
BAIL_ON_FAILURE(hr);
//
// See if the ASQ control is in there.
//
while (ppServerControls
&& ppServerControls[dwCtr]
&& wcscmp(
ppServerControls[dwCtr]->ldctl_oid,
LDAP_SERVER_ASQ_OID_W
) != 0) {
dwCtr++;
}
if (!ppServerControls || !ppServerControls[dwCtr]) {
//
// Could not get the control
//
BAIL_ON_FAILURE(hr = E_ADS_PROPERTY_NOT_FOUND);
}
//
// Get the info we need.
//
berVal.bv_len = ppServerControls[dwCtr]->ldctl_value.bv_len;
berVal.bv_val = ppServerControls[dwCtr]->ldctl_value.bv_val;
pBer = ber_init(&berVal);
if (ber_scanf(pBer, "{e}", &retval) != NO_ERROR) {
BAIL_ON_FAILURE(hr = E_FAIL);
}
//
// Test for non-fatal error codes
//
if (retval == LDAP_AFFECTS_MULTIPLE_DSAS)
phSearchInfo->_fNonFatalErrors = TRUE;
error :
if (ppServerControls) {
ldap_controls_free(ppServerControls);
}
if (pBer) {
ber_free(pBer, 1);
}
RRETURN(hr);
}
HRESULT
StoreDirSyncCookie(
LDAPMessage *pLDAPMsg,
PLDAP_SEARCHINFO phSearchInfo
)
{
HRESULT hr = S_OK;
PADS_PROV_SPECIFIC pProvSpecific = NULL;
PLDAPControl *ppServerControls = NULL;
DWORD dwCtr = 0;
BERVAL berVal;
BerElement *pBer = NULL;
PBERVAL pBerVal = NULL;
DWORD dwSize;
BOOL fMoreData = FALSE;
if (!pLDAPMsg) {
RRETURN(S_OK);
}
phSearchInfo->_fMoreDirSync = FALSE;
//
// Build the new value and then assign it to the searchpref
// information. That way, if there are errors we wont loose
// the last cookie.
//
hr = LdapParseResult(
phSearchInfo->_pConnection,
pLDAPMsg,
NULL, // ret code
NULL, // matched dn's
NULL, // err msg's
NULL, // referrals
&ppServerControls,
FALSE // freeIt
);
BAIL_ON_FAILURE(hr);
//
// See if the dirsync control is in there.
//
while (ppServerControls
&& ppServerControls[dwCtr]
&& wcscmp(
ppServerControls[dwCtr]->ldctl_oid,
LDAP_SERVER_DIRSYNC_OID_W
) != 0) {
dwCtr++;
}
if (!ppServerControls || !ppServerControls[dwCtr]) {
//
// Could not get the control
//
BAIL_ON_FAILURE(hr = E_ADS_PROPERTY_NOT_FOUND);
}
//
// Get the info we need.
//
berVal.bv_len = ppServerControls[dwCtr]->ldctl_value.bv_len;
berVal.bv_val = ppServerControls[dwCtr]->ldctl_value.bv_val;
pBer = ber_init(&berVal);
ber_scanf(pBer, "{iiO}", &fMoreData, &dwSize, &pBerVal);
phSearchInfo->_fMoreDirSync = fMoreData;
pProvSpecific = (PADS_PROV_SPECIFIC)
AllocADsMem(sizeof(ADS_PROV_SPECIFIC));
if (!pProvSpecific) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pProvSpecific->lpValue = (LPBYTE) AllocADsMem(pBerVal->bv_len);
if (!pProvSpecific->lpValue) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pProvSpecific->dwLength = pBerVal->bv_len;
memcpy(pProvSpecific->lpValue, (LPBYTE) pBerVal->bv_val, pBerVal->bv_len);
//
// At this point it is safe to clear the Info on the dirsync control
//
if (phSearchInfo->_SearchPref._pProvSpecific) {
if (phSearchInfo->_SearchPref._pProvSpecific->lpValue) {
FreeADsMem(phSearchInfo->_SearchPref._pProvSpecific->lpValue);
}
FreeADsMem(phSearchInfo->_SearchPref._pProvSpecific);
}
phSearchInfo->_SearchPref._pProvSpecific = pProvSpecific;
error :
if (ppServerControls) {
ldap_controls_free(ppServerControls);
}
if (FAILED(hr)) {
//
// Handle the Provider Specific struct if applicable.
//
if (pProvSpecific) {
if (pProvSpecific->lpValue) {
FreeADsMem(pProvSpecific->lpValue);
}
FreeADsMem(pProvSpecific);
}
}
if (pBerVal) {
ber_bvfree(pBerVal);
}
if (pBer) {
ber_free(pBer, 1);
}
RRETURN(hr);
}
HRESULT
BerEncodeReplicationCookie(
PBYTE pCookie,
DWORD dwLen,
PBERVAL *ppBerVal
)
{
HRESULT hr = E_FAIL;
BerElement *pBer = NULL;
pBer = ber_alloc_t(LBER_USE_DER);
if (!pBer) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
//
// flag - set to zero, so order of parent & child objects is not important
//
if (ber_printf(pBer, "{iio}", 0, MAX_BYTES, pCookie, dwLen) == -1) {
BAIL_ON_FAILURE(hr = E_FAIL);
}
//
// Pull data from the BerElement into a BERVAL struct.
// Caller needs to free ppBerVal.
//
if (ber_flatten(pBer, ppBerVal) != 0) {
BAIL_ON_FAILURE(hr = E_FAIL);
}
hr = S_OK;
error:
if (pBer) {
ber_free(pBer,1);
}
return hr;
}
HRESULT
BerEncodeAttribScopedControlValue(
LPCWSTR pAttribScoped,
PBERVAL *ppBerVal
)
{
HRESULT hr = S_OK;
BerElement *pBer = NULL;
LPSTR pszAttribute = NULL;
pBer = ber_alloc_t(LBER_USE_DER);
if (!pBer) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
//
// Translate the Unicode strings to UTF-8
//
hr = UnicodeToUTF8String(pAttribScoped, &pszAttribute);
BAIL_ON_FAILURE(hr);
//
// BER-encode the attributeScopedQueryRequestControlValue
//
if (ber_printf(pBer, "{s}", pszAttribute) == -1) {
BAIL_ON_FAILURE(hr = E_FAIL);
}
//
// Pull data from the BerElement into a BERVAL struct.
// Caller needs to free ppBerVal.
//
if (ber_flatten(pBer, ppBerVal) != 0) {
BAIL_ON_FAILURE(hr = E_FAIL);
}
error:
if (pBer) {
ber_free(pBer,1);
}
if (pszAttribute)
FreeADsMem(pszAttribute);
return hr;
}
//
// This is called only by ADsGetMoreResultsDirSync.
//
HRESULT
ADsGetMoreResultsDirSyncHelper(
IN PLDAP_SEARCHINFO phSearchInfo,
CCredentials& Credentials
)
{
HRESULT hr = S_OK;
DWORD dwError;
LPWSTR pszLDAPPath;
WCHAR pszErrorBuf[MAX_PATH], pszNameBuf[MAX_PATH];
ULONG totalCount;
int resType;
ADsAssert(phSearchInfo);
//
// If the searchpref is not dirsync, abandon has been called
// or if the cookie indicated that there is no more data then
// we should return right away.
//
if (!phSearchInfo->_SearchPref._fDirSync
|| phSearchInfo->_fAbandon
|| !phSearchInfo->_fMoreDirSync) {
RRETURN(S_ADS_NOMORE_ROWS);
}
//
// We need to update the controls
//
hr = AddSearchControls(
phSearchInfo,
Credentials
);
BAIL_ON_FAILURE(hr);
//
// Need to allocate more messages in the buffer
//
if ( phSearchInfo->_SearchPref._fCacheResults ) {
ADsAssert(phSearchInfo->_dwCurrResult
== phSearchInfo->_dwMaxResultGot);
phSearchInfo->_dwCurrResult++;
phSearchInfo->_dwMaxResultGot++;
if (phSearchInfo->_dwCurrResult >= phSearchInfo->_cSearchResults) {
//
// Need to allocate more memory for handles
//
phSearchInfo->_pSearchResults = (LDAPMessage **) ReallocADsMem(
(void *) phSearchInfo->_pSearchResults,
sizeof(LDAPMessage *) *
phSearchInfo->_cSearchResults,
sizeof(LDAPMessage *) *
(phSearchInfo->_cSearchResults +
NO_LDAP_RESULT_HANDLES));
if(!phSearchInfo->_pSearchResults) {
hr = E_OUTOFMEMORY;
phSearchInfo->_dwCurrResult--;
phSearchInfo->_dwMaxResultGot--;
goto error;
}
phSearchInfo->_cSearchResults += NO_LDAP_RESULT_HANDLES;
}
}
else {
//
// Release and use the same space to store the next result.
//
LdapMsgFree(phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]);
}
//
// Async and sync searches need to be handled differently.
//
if (phSearchInfo->_SearchPref._fAsynchronous) {
//
// Asynchronous search.
//
hr = LdapSearchExt(
phSearchInfo->_pConnection,
phSearchInfo->_pszBindContextDn,
phSearchInfo->_SearchPref._dwSearchScope,
phSearchInfo->_pszSearchFilter,
phSearchInfo->_ppszAttrs,
phSearchInfo->_SearchPref._fAttrsOnly,
phSearchInfo->_ServerControls,
phSearchInfo->_ClientControls,
phSearchInfo->_SearchPref._dwPagedTimeLimit,
phSearchInfo->_SearchPref._dwSizeLimit,
&phSearchInfo->_currMsgId
);
BAIL_ON_FAILURE(hr);
phSearchInfo->_fLastResult = FALSE;
//
// Wait for atleast one result
//
hr = LdapResult(
phSearchInfo->_pConnection,
phSearchInfo->_currMsgId,
LDAP_MSG_RECEIVED,
phSearchInfo->_SearchPref._timeout.tv_sec ?
&phSearchInfo->_SearchPref._timeout : NULL,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&resType
);
if ((hr == HRESULT_FROM_WIN32(ERROR_DS_INVALID_DN_SYNTAX)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_NAMING_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_OBJ_CLASS_VIOLATION)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_FILTER_UNKNOWN)) ||
(hr == HRESULT_FROM_WIN32(ERROR_DS_PARAM_ERROR)) ||
(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER))) {
phSearchInfo->_fLastResult = TRUE;
RRETURN(S_ADS_NOMORE_ROWS);
}
else {
//
// Only if there are zero rows returned, return the error,
// otherwise, store the error and return when GetNextRow is
// called for the last time
//
if (FAILED(hr) &&
(LdapCountEntries( phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult])
== 0)) {
BAIL_ON_FAILURE(hr);
}
else {
phSearchInfo->_hrLastSearch = hr;
hr = S_OK;
}
}
phSearchInfo->_fLastPage = TRUE;
}
else {
//
// Synchronous search
//
hr = LdapSearchExtS(
phSearchInfo->_pConnection,
phSearchInfo->_pszBindContextDn,
phSearchInfo->_SearchPref._dwSearchScope,
phSearchInfo->_pszSearchFilter,
phSearchInfo->_ppszAttrs,
phSearchInfo->_SearchPref._fAttrsOnly,
phSearchInfo->_ServerControls,
phSearchInfo->_ClientControls,
(phSearchInfo->_SearchPref._timeout.tv_sec == 0) ?
NULL :
&phSearchInfo->_SearchPref._timeout,
phSearchInfo->_SearchPref._dwSizeLimit,
&phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]
);
phSearchInfo->_fLastResult = TRUE;
phSearchInfo->_fLastPage = TRUE;
}
//
// Only if there are zero rows returned, return the error,
// otherwise, store the error and return when GetNextRow is
// called for the last time
//
if (FAILED(hr) &&
(LdapCountEntries( phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult]) == 0)) {
BAIL_ON_FAILURE(hr);
}
else {
phSearchInfo->_hrLastSearch = hr;
hr = S_OK;
}
error:
RRETURN(hr);
}
//
// This function is very similar to GetMoreResults except that
// it will issue a new search if applicable for the dirsync control.
//
HRESULT
ADsGetMoreResultsDirSync(
IN PLDAP_SEARCHINFO phSearchInfo,
CCredentials& Credentials
)
{
HRESULT hr = S_OK;
BOOL fTryAndGetResults = TRUE;
//
// If the searchpref is not dirsync, abandon has been called
// or if the cookie indicated that there is no more data then
// we should return right away.
//
if (!phSearchInfo->_SearchPref._fDirSync
|| phSearchInfo->_fAbandon
|| !phSearchInfo->_fMoreDirSync) {
RRETURN(S_ADS_NOMORE_ROWS);
}
while (fTryAndGetResults) {
fTryAndGetResults = FALSE;
hr = ADsGetMoreResultsDirSyncHelper(
phSearchInfo,
Credentials
);
BAIL_ON_FAILURE(hr);
StoreDirSyncCookie(
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
phSearchInfo
);
if (hr == S_ADS_NOMORE_ROWS && phSearchInfo->_fMoreDirSync) {
fTryAndGetResults = TRUE;
}
//
// Now we want to see if the first row was valid. We could
// get back an entry but then not have any rows, just a cookie
//
if (!fTryAndGetResults) {
hr = LdapFirstEntry(
phSearchInfo->_pConnection,
phSearchInfo->_pSearchResults[phSearchInfo->_dwCurrResult],
&phSearchInfo->_pCurrentRow
);
BAIL_ON_FAILURE(hr);
if(phSearchInfo->_pCurrentRow) {
phSearchInfo->_pFirstAttr = NULL;
phSearchInfo->_fBefFirstRow = FALSE;
hr = S_OK;
}
else {
hr = S_ADS_NOMORE_ROWS;
if (phSearchInfo->_fMoreDirSync) {
fTryAndGetResults = TRUE;
}
}
} // if !Try and get more results.
} // while try and get more results.
error :
RRETURN(hr);
}
//+---------------------------------------------------------------------------
// Function: LdapInitializeSearchPreferences - Exported helper routine.
//
// Synopsis: Initializes the search preferences struc to the default values.
// With this function we can isolate the code in one place.
//
// Arguments: pSearchPrefs - Ptr to search prefs being initialized.
// fCacheResults - The cache results pref is set to this.
//
// Returns: N/A.
//
// Modifies: pSearchPrefs.
//
//----------------------------------------------------------------------------
void
LdapInitializeSearchPreferences(
LDAP_SEARCH_PREF *pSearchPrefs,
BOOL fCacheResults
)
{
ADsAssert(pSearchPrefs);
pSearchPrefs->_fAsynchronous = FALSE;
pSearchPrefs->_dwDerefAliases = FALSE;
pSearchPrefs->_dwSizeLimit = 0;
pSearchPrefs->_dwTimeLimit = 0;
pSearchPrefs->_fAttrsOnly = FALSE;
pSearchPrefs->_dwSearchScope = LDAP_SCOPE_SUBTREE;
pSearchPrefs->_timeout.tv_sec = 0;
pSearchPrefs->_timeout.tv_usec = 0;
pSearchPrefs->_dwPageSize = 0;
pSearchPrefs->_dwPagedTimeLimit = 0;
pSearchPrefs->_dwChaseReferrals = ADS_CHASE_REFERRALS_EXTERNAL;
pSearchPrefs->_pSortKeys = NULL;
pSearchPrefs->_nSortKeys = 0;
pSearchPrefs->_fDirSync = FALSE;
pSearchPrefs->_pProvSpecific = NULL;
pSearchPrefs->_fTombStone = FALSE;
pSearchPrefs->_fCacheResults = fCacheResults;
pSearchPrefs->_pVLVInfo = NULL;
pSearchPrefs->_pAttribScoped = NULL;
pSearchPrefs->_fSecurityDescriptorControl = FALSE;
}
//+---------------------------------------------------------------------------
// Function: ADsHelperGetCurrentRowMessage - used for Umi Search support.
//
// Synopsis: This returns the current row and the handle of the search.
// Neither are refCounted but this should not matter cause these
// will no longer be in use by the caller beyond the scope of
// the search (before the search is "closed", the handle and
// message that are got from this search will no longer be in
// use).
//
// Arguments: hSearchHandle - Handle to the search.
// ppAdsLdp - Pointer to hold returned lda handle.
// ppLdapMsg - Pointer to hold the current "rows" msg.
//
// Returns: S_OK or any appropriate error code.
//
// Modifies: ppAdsLdp and ppLdapMsg if successful.
//
//----------------------------------------------------------------------------
HRESULT
ADsHelperGetCurrentRowMessage(
IN ADS_SEARCH_HANDLE hSearchHandle,
OUT PADSLDP *ppAdsLdp,
OUT LDAPMessage **ppLdapMsg
)
{
HRESULT hr = S_OK;
PLDAP_SEARCHINFO phSearchInfo = (PLDAP_SEARCHINFO) hSearchHandle;
if( !phSearchInfo
|| !phSearchInfo->_pSearchResults) {
RRETURN (E_ADS_BAD_PARAMETER);
}
if (!phSearchInfo->_pConnection || !phSearchInfo->_pCurrentRow) {
//
// Dont have the info we need
//
RRETURN(E_FAIL);
}
else {
//
// We have the handle and the row we need.
//
*ppAdsLdp = phSearchInfo->_pConnection;
*ppLdapMsg = phSearchInfo->_pCurrentRow;
}
RRETURN(hr);
}