You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4775 lines
145 KiB
4775 lines
145 KiB
//---------------------------------------------------------------------------;
|
|
//
|
|
//
|
|
// 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;
|
|
DWORD dwDirsyncFlag;
|
|
DWORD dwExtendedDNOption;
|
|
|
|
int iDirsyncFlagIndex = -1;
|
|
|
|
|
|
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);
|
|
if(!pLdapPref->_pSortKeys[j].sk_attrtype && pSortKeyArray[j].pszAttrType)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
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;
|
|
|
|
case ADS_SEARCHPREF_DIRSYNC_FLAG:
|
|
// sanity check of user supplied data
|
|
//
|
|
dwDirsyncFlag = pSearchPrefs[i].vValue.Integer;
|
|
|
|
if(dwDirsyncFlag & (~(LDAP_DIRSYNC_OBJECT_SECURITY |
|
|
LDAP_DIRSYNC_ANCESTORS_FIRST_ORDER |
|
|
LDAP_DIRSYNC_PUBLIC_DATA_ONLY |
|
|
LDAP_DIRSYNC_INCREMENTAL_VALUES)))
|
|
{
|
|
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
|
|
fWarning = TRUE;
|
|
continue;
|
|
|
|
}
|
|
|
|
// set the flag value
|
|
pLdapPref->_dwDirSyncFlag = dwDirsyncFlag;
|
|
|
|
iDirsyncFlagIndex = i;
|
|
break;
|
|
|
|
case ADS_SEARCHPREF_EXTENDED_DN:
|
|
// sanity check of user supplied data
|
|
//
|
|
dwExtendedDNOption = pSearchPrefs[i].vValue.Integer;
|
|
|
|
if(dwExtendedDNOption != 0 && dwExtendedDNOption != 1)
|
|
{
|
|
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREFVALUE;
|
|
fWarning = TRUE;
|
|
continue;
|
|
}
|
|
|
|
// set the option
|
|
pLdapPref->_fExtendedDNControl = TRUE;
|
|
pLdapPref->_dwExtendedDnOption = dwExtendedDNOption;
|
|
break;
|
|
|
|
|
|
default:
|
|
pSearchPrefs[i].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
|
|
fWarning = TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// if dirsyncflag is specified, but using dirsync control is not, then we should indicate error
|
|
if(!pLdapPref->_fDirSync && iDirsyncFlagIndex != -1)
|
|
{
|
|
pSearchPrefs[iDirsyncFlagIndex].dwStatus = ADS_STATUS_INVALID_SEARCHPREF;
|
|
fWarning = TRUE;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
phSearchInfo->_fDNPresent = FALSE;
|
|
phSearchInfo->_fDNReturned = FALSE;
|
|
|
|
if (dwNumberAttributes == -1) {
|
|
//
|
|
// Specifies returning all attributes
|
|
//
|
|
|
|
phSearchInfo->_ppszAttrs = NULL;
|
|
phSearchInfo->_pszAttrNameBuffer = NULL;
|
|
phSearchInfo->_fADsPathPresent = TRUE;
|
|
if(LdapPref._fDirSync)
|
|
{
|
|
phSearchInfo->_fDNPresent = 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 if(LdapPref._fDirSync && _wcsicmp(ppszAttrs[j], L"distinguishedName") == 0) {
|
|
//
|
|
// This attribute need not be sent
|
|
//
|
|
|
|
phSearchInfo->_fDNPresent = 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;
|
|
phSearchInfo->_pBerValExtendedDN = 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;
|
|
HRESULT hr = S_OK;
|
|
int ldaperr = 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
|
|
//
|
|
|
|
if(dwStatus != 0)
|
|
{
|
|
// the abandon call fails
|
|
phSearchInfo->_fAbandon = FALSE;
|
|
ldaperr = LdapGetLastError();
|
|
CheckAndSetExtendedError( phSearchInfo->_pConnection->LdapHandle, &hr, ldaperr);
|
|
}
|
|
else
|
|
{
|
|
phSearchInfo->_fAbandon = TRUE;
|
|
}
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
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, extended dn & 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->_pBerValExtendedDN)
|
|
{
|
|
ber_bvfree(phSearchInfo->_pBerValExtendedDN);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// reset the value
|
|
phSearchInfo->_fADsPathReturned = FALSE;
|
|
phSearchInfo->_fDNReturned = FALSE;
|
|
|
|
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);
|
|
}
|
|
|
|
// reset the value
|
|
phSearchInfo->_fADsPathReturned = FALSE;
|
|
phSearchInfo->_fDNReturned = FALSE;
|
|
|
|
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;
|
|
LDAPMessage **pTemp = NULL;
|
|
|
|
|
|
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
|
|
//
|
|
pTemp = (LDAPMessage **) ReallocADsMem(
|
|
(void *) phSearchInfo->_pSearchResults,
|
|
sizeof(LDAPMessage *) *
|
|
phSearchInfo->_cSearchResults,
|
|
sizeof(LDAPMessage *) *
|
|
(phSearchInfo->_cSearchResults +
|
|
NO_LDAP_RESULT_HANDLES));
|
|
if(!pTemp) {
|
|
hr = E_OUTOFMEMORY;
|
|
phSearchInfo->_dwCurrResult--;
|
|
phSearchInfo->_dwMaxResultGot--;
|
|
goto error;
|
|
}
|
|
phSearchInfo->_pSearchResults = pTemp;
|
|
pTemp = NULL;
|
|
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
|
|
//
|
|
pTemp = (LDAPMessage **) ReallocADsMem(
|
|
(void *) phSearchInfo->_pSearchResults,
|
|
sizeof(LDAPMessage *) *
|
|
phSearchInfo->_cSearchResults,
|
|
sizeof(LDAPMessage *) *
|
|
(phSearchInfo->_cSearchResults +
|
|
NO_LDAP_RESULT_HANDLES));
|
|
if(!pTemp) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
phSearchInfo->_pSearchResults = pTemp;
|
|
pTemp = NULL;
|
|
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 = NULL;
|
|
LPWSTR pszADsPathName = NULL;
|
|
DWORD dwSyntaxId;
|
|
DWORD dwError;
|
|
WCHAR pszErrorBuf[MAX_PATH], pszNameBuf[MAX_PATH];
|
|
PADS_VLV pVLV = NULL;
|
|
LPWSTR pszTempDN = 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);
|
|
|
|
|
|
// if extended_dn control is used, we need to get the normal dn format out for ADsPath
|
|
if(phSearchInfo->_SearchPref._fExtendedDNControl)
|
|
{
|
|
hr = HelpGetNormalDN(pszDn, &pszTempDN);
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
else
|
|
{
|
|
pszTempDN = pszDn;
|
|
}
|
|
|
|
//
|
|
// Build the ADsPath
|
|
//
|
|
|
|
hr = BuildADsPathFromLDAPPath(
|
|
phSearchInfo->_pszADsPathContext,
|
|
pszTempDN,
|
|
&pszADsPathName
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
LdapMemFree(pszDn);
|
|
pszDn = NULL;
|
|
|
|
|
|
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 DistinguishedName
|
|
//
|
|
if(phSearchInfo->_fDNPresent && !_wcsicmp(pszColumnName, L"distinguishedName"))
|
|
{
|
|
//
|
|
// Get the DN of the entry.
|
|
//
|
|
hr = LdapGetDn(
|
|
phSearchInfo->_pConnection,
|
|
phSearchInfo->_pCurrentRow,
|
|
&pszDn
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
pColumn->pADsValues = (PADSVALUE) AllocADsMem(sizeof(ADSVALUE));
|
|
if (!pColumn->pADsValues)
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
|
|
pColumn->dwADsType = ADSTYPE_DN_STRING;
|
|
pColumn->dwNumValues = 1;
|
|
pColumn->pADsValues[0].dwType = ADSTYPE_DN_STRING;
|
|
|
|
pColumn->pADsValues[0].CaseIgnoreString = AllocADsStr(pszDn);
|
|
if (!pColumn->pADsValues[0].CaseIgnoreString)
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
|
|
LdapMemFree(pszDn);
|
|
|
|
RRETURN(S_OK);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// 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);
|
|
|
|
if(pszDn)
|
|
{
|
|
LdapMemFree(pszDn);
|
|
}
|
|
|
|
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->_fDNPresent || phSearchInfo->_fADsPathPresent) {
|
|
|
|
if(phSearchInfo->_fDNPresent)
|
|
{
|
|
if(!phSearchInfo->_fDNReturned)
|
|
{
|
|
*ppszColumnName = AllocADsStr(L"distinguishedName");
|
|
if(!*ppszColumnName)
|
|
{
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
phSearchInfo->_fDNReturned = TRUE;
|
|
|
|
// got the column name, just return
|
|
hr = S_OK;
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
// just set hr equal to no more column here
|
|
hr = S_ADS_NOMORE_COLUMNS;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If ADsPath was specified return it as the last column
|
|
//
|
|
|
|
if(phSearchInfo->_fADsPathPresent)
|
|
{
|
|
|
|
if (!phSearchInfo->_fADsPathReturned) {
|
|
|
|
*ppszColumnName = AllocADsStr(L"ADsPath");
|
|
if(!*ppszColumnName)
|
|
{
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
phSearchInfo->_fADsPathReturned = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
else {
|
|
|
|
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;
|
|
|
|
case ADS_SEARCHPREF_DIRSYNC_FLAG:
|
|
if(SearchPref.vValue.dwType != ADSTYPE_INTEGER)
|
|
return FALSE;
|
|
break;
|
|
|
|
case ADS_SEARCHPREF_EXTENDED_DN:
|
|
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;
|
|
PLDAPControl pExtendedCtrl = 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._fExtendedDNControl)
|
|
{
|
|
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, extended dn 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,
|
|
phSearchInfo->_SearchPref._dwDirSyncFlag
|
|
);
|
|
|
|
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;
|
|
}
|
|
|
|
// process the extended dn control
|
|
if(phSearchInfo->_SearchPref._fExtendedDNControl)
|
|
{
|
|
pExtendedCtrl = (LDAPControl *) AllocADsMem(sizeof(LDAPControl));
|
|
if(!pExtendedCtrl)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
hr = BerEncodeExtendedDNControlValue(phSearchInfo->_SearchPref._dwExtendedDnOption, &pBerVal);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
pExtendedCtrl->ldctl_oid = LDAP_SERVER_EXTENDED_DN_OID_W;
|
|
pExtendedCtrl->ldctl_value.bv_len = pBerVal->bv_len;
|
|
pExtendedCtrl->ldctl_value.bv_val = pBerVal->bv_val;
|
|
pExtendedCtrl->ldctl_iscritical = TRUE;
|
|
|
|
//
|
|
// Clear the info in the search handle if applicable
|
|
//
|
|
if(phSearchInfo->_pBerValExtendedDN)
|
|
{
|
|
ber_bvfree(phSearchInfo->_pBerValExtendedDN);
|
|
}
|
|
|
|
phSearchInfo->_pBerValExtendedDN = pBerVal;
|
|
|
|
ppServerControls[dwCurControl++] = pExtendedCtrl;
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
if(pExtendedCtrl)
|
|
{
|
|
FreeADsMem(pExtendedCtrl);
|
|
}
|
|
|
|
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,
|
|
DWORD dwFlag
|
|
)
|
|
{
|
|
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}", dwFlag, 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;
|
|
}
|
|
|
|
HRESULT
|
|
BerEncodeExtendedDNControlValue(
|
|
DWORD dwOption,
|
|
PBERVAL *ppBerVal
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BerElement *pBer = NULL;
|
|
|
|
pBer = ber_alloc_t(LBER_USE_DER);
|
|
if (!pBer) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// BER-encode the extendeddncontrolvalue
|
|
//
|
|
if (ber_printf(pBer, "{i}", dwOption) == -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);
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
BerEncodingQuotaControl(
|
|
PBYTE pSid,
|
|
DWORD dwSidLength,
|
|
PBERVAL *ppBerVal
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
BerElement *pBer = NULL;
|
|
|
|
pBer = ber_alloc_t(LBER_USE_DER);
|
|
if (!pBer) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
|
|
if (ber_printf(pBer, "{o}", pSid, dwSidLength) == -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;
|
|
}
|
|
|
|
|
|
//
|
|
// 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;
|
|
LDAPMessage **pTemp = NULL;
|
|
|
|
|
|
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
|
|
//
|
|
pTemp = (LDAPMessage **) ReallocADsMem(
|
|
(void *) phSearchInfo->_pSearchResults,
|
|
sizeof(LDAPMessage *) *
|
|
phSearchInfo->_cSearchResults,
|
|
sizeof(LDAPMessage *) *
|
|
(phSearchInfo->_cSearchResults +
|
|
NO_LDAP_RESULT_HANDLES));
|
|
if(!pTemp) {
|
|
hr = E_OUTOFMEMORY;
|
|
phSearchInfo->_dwCurrResult--;
|
|
phSearchInfo->_dwMaxResultGot--;
|
|
goto error;
|
|
}
|
|
phSearchInfo->_pSearchResults = pTemp;
|
|
pTemp = NULL;
|
|
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;
|
|
pSearchPrefs->_dwDirSyncFlag = 0;
|
|
pSearchPrefs->_fExtendedDNControl = FALSE;
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
HelpGetNormalDN(
|
|
LPWSTR pszDn,
|
|
LPWSTR* ppszTempDn
|
|
)
|
|
{
|
|
WCHAR* pszChr = NULL;
|
|
|
|
// the extended dn format is <GUID=...>;<SID=...>;distinguishedName. <SID=...> is optional part
|
|
pszChr = wcschr(pszDn, L';');
|
|
|
|
// <GUID=...> part is always present
|
|
ADsAssert(pszChr);
|
|
|
|
if(!pszChr)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if(pszChr[1] == '<')
|
|
{
|
|
// means <SID=...> part is present
|
|
pszDn = pszChr + 1;
|
|
pszChr = wcschr(pszDn, L';');
|
|
|
|
ADsAssert(pszChr);
|
|
if(!pszChr)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
}
|
|
|
|
*ppszTempDn = pszChr + 1;
|
|
return S_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// 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);
|
|
}
|
|
|