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

4165 lines
96 KiB
C++

//----------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996
//
// File: cgenobj.cxx
//
// Contents: Microsoft ADs LDAP Provider Generic Object
//
//
// History: 08-30-96 yihsins Created.
//
//---------------------------------------- ------------------------------------
#include "ldap.hxx"
#pragma hdrstop
#ifdef __cplusplus
extern "C"
#else
extern
#endif
HRESULT
AdsTypeToPropVariant2(
PADSVALUE pAdsValues,
DWORD dwNumValues,
VARIANT * pVariant,
LPWSTR pszServerName,
CCredentials* pCredentials,
BOOL fNTDSType
);
#ifdef __cplusplus
extern "C"
#else
extern
#endif
HRESULT
PropVariantToAdsType2(
PVARIANT pVariant,
DWORD dwNumVariant,
PADSVALUE *ppAdsValues,
PDWORD pdwNumValues,
LPWSTR pszServerName,
CCredentials* pCredentials,
BOOL fNTDSType
);
//
// Helper routine that handles setting the sticky server private
// option when the input is an array.
//
HRESULT
SetStickyServerWithDomain(
PVARIANT pvProp
);
// Class CLDAPGenObject
DEFINE_IDispatch_ExtMgr_Implementation(CLDAPGenObject)
DEFINE_IADs_Shorter_Implementation(CLDAPGenObject)
//
// This is a useful function
//
HRESULT GetIntegerFromVariant(
VARIANT* pvProp,
DWORD* pdwValue
);
typedef struct _classeshierarchylist {
LPWSTR pszClassName;
struct _classeshierarchylist *pNext;
} ClassesHierarchyList, *PClassesHierarchyList;
//
// Helper to Trace The Tree for a class
//
HRESULT
TraceTreeForClass(
BSTR Parent,
BSTR CommonName,
LPWSTR pszClassName,
CCredentials& Credentials,
PWCHAR **pppszNameArr,
PLONG plnNumElements
);
HRESULT
AddToClassesList(
VARIANT vBstrVal,
LPWSTR *ppszCurClass,
PClassesHierarchyList *pClassListhead,
PLONG plnNumItems
);
CLDAPGenObject::CLDAPGenObject():
_pExtMgr(NULL),
_pPropertyCache( NULL ),
_pDispMgr( NULL ),
_pszLDAPServer(NULL),
_pszLDAPDn(NULL),
_pLdapHandle( NULL ),
_dwOptReferral((DWORD) LDAP_CHASE_EXTERNAL_REFERRALS),
_dwPageSize(99),
_dwCorePropStatus(0),
_fRangeRetrieval(FALSE)
{
VariantInit(&_vFilter);
VariantInit(&_vHints);
_seInfo = OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION;
_fExplicitSecurityMask = FALSE;
LdapInitializeSearchPreferences(&_SearchPref, TRUE);
ENLIST_TRACKING(CLDAPGenObject);
}
//
// fClassDefaulted indicates if the class has been defaulted
// because of a fast bind flag or otherwise.
//
HRESULT
CLDAPGenObject::CreateGenericObject(
BSTR Parent,
BSTR CommonName,
BSTR LdapClassName,
CCredentials& Credentials,
DWORD dwObjectState,
REFIID riid,
void **ppvObj,
BOOL fClassDefaulted,
BOOL fNoQI // defaulted to FALSE
)
{
//
// Call into the fully featured Create
//
LPWSTR pszClassNames[2];
HRESULT hr = S_OK;
PWCHAR *ppListOfNames;
LONG lnNumNames = 0;
pszClassNames[0] = LdapClassName;
pszClassNames[1] = NULL;
hr = TraceTreeForClass(
Parent,
CommonName,
LdapClassName,
Credentials,
&ppListOfNames,
&lnNumNames
);
if (FAILED(hr)) {
//
// Default to just the class name given
//
RRETURN(CreateGenericObject(
Parent,
CommonName,
pszClassNames,
1,
Credentials,
dwObjectState,
riid,
ppvObj,
fClassDefaulted,
fNoQI
)
);
}
else {
//
// Create with all the classes specified
//
hr = CreateGenericObject(
Parent,
CommonName,
ppListOfNames,
lnNumNames,
Credentials,
dwObjectState,
riid,
ppvObj,
fClassDefaulted,
fNoQI
);
for (long i = 0; i < lnNumNames; i++) {
if (ppListOfNames[i]) {
FreeADsStr(ppListOfNames[i]);
}
}
if (ppListOfNames) {
FreeADsMem(ppListOfNames);
}
RRETURN(hr);
}
}
//+------------------------------------------------------------------------
//
// Function: CLDAPGenObject::CreateGenericObject
//
// Synopsis: Does all the work and actually creates the object.
// Difference from the overlaoded member being that it accepts an
// array of values for the class name so that it can load
// extensions for all the classes in the inheritance hierarchy.
//
// Arguments:
//
//-------------------------------------------------------------------------
HRESULT
CLDAPGenObject::CreateGenericObject(
BSTR Parent,
BSTR CommonName,
LPWSTR LdapClassNames[],
long lnNumClasses,
CCredentials& Credentials,
DWORD dwObjectState,
REFIID riid,
void **ppvObj,
BOOL fClassDefaulted,
BOOL fNoQI // will return cgenobject if set to TRUE, defaulted FALSE
)
{
CLDAPGenObject FAR * pGenObject = NULL;
HRESULT hr = S_OK;
DWORD dwCtr = 0;
LPWSTR pszBaseClassName = GET_BASE_CLASS(LdapClassNames, lnNumClasses);
hr = AllocateGenObject(pszBaseClassName, Credentials, &pGenObject);
BAIL_ON_FAILURE(hr);
ADsAssert(pGenObject->_pDispMgr);
pGenObject->_Credentials = Credentials;
hr = pGenObject->InitializeCoreObject(
Parent,
CommonName,
pszBaseClassName,
CLSID_LDAPGenObject,
dwObjectState
);
BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2(
pGenObject->_ADsPath,
&pGenObject->_pszLDAPServer,
&pGenObject->_pszLDAPDn,
&pGenObject->_dwPort
);
BAIL_ON_FAILURE(hr);
//
// At this point update the info in the property cache
//
hr = pGenObject->_pPropertyCache->SetObjInformation(
&(pGenObject->_Credentials),
pGenObject->_pszLDAPServer,
pGenObject->_dwPort
);
BAIL_ON_FAILURE(hr);
//
// Create and Load 3rd party extensions and the extension mgr.
// This should be done after initilaization of core and generic object s.t
// 1) an extension writer can access info on IADs (e.g _Parent) etc
// during extension creation, if he wants to.
// 2) we shouldn't waste the effort to create and load extension
// objects if fialure in the aggregator's creation
//
hr = CADsExtMgr::CreateExtMgr(
(IADs *)pGenObject,
pGenObject->_pDispMgr,
LdapClassNames,
lnNumClasses,
&(pGenObject->_Credentials),
&pGenObject->_pExtMgr
);
BAIL_ON_FAILURE(hr);
ADsAssert(pGenObject->_pExtMgr);
hr = LdapOpenObject(
pGenObject->_pszLDAPServer,
pGenObject->_pszLDAPDn,
&(pGenObject->_pLdapHandle),
pGenObject->_Credentials,
pGenObject->_dwPort
);
BAIL_ON_FAILURE(hr);
if (!fClassDefaulted) {
//
// Update the status to reflect we do not need to go
// on the wire to get the class
//
pGenObject->_dwCorePropStatus |= LDAP_CLASS_VALID;
}
if (fNoQI) {
*ppvObj = (void *) pGenObject;
}
else if (Credentials.GetAuthFlags() & ADS_AUTH_RESERVED) {
//
// Call is from umi so we need to create the umi object.
//
hr = ((CCoreADsObject*)pGenObject)->InitUmiObject(
IntfPropsGeneric,
pGenObject->_pPropertyCache,
(IADs *) pGenObject,
(IADs *) pGenObject,
IID_IUnknown,
ppvObj,
&(pGenObject->_Credentials),
pGenObject->_dwPort,
pGenObject->_pszLDAPServer,
pGenObject->_pszLDAPDn,
pGenObject->_pLdapHandle,
pGenObject->_pExtMgr
);
BAIL_ON_FAILURE(hr);
RRETURN(S_OK);
} else {
//
// Need the appropriate interface.
//
hr = pGenObject->QueryInterface(riid, ppvObj);
BAIL_ON_FAILURE(hr);
pGenObject->Release();
}
BAIL_ON_FAILURE(hr);
RRETURN(hr);
error:
*ppvObj = NULL;
delete pGenObject;
RRETURN_EXP_IF_ERR(hr);
}
//+---------------------------------------------------------------------------
// Function: CLDAPGenObject::CreateGenericObject (static constructor), this
// constructor is used by Umi Searches only.
//
// Synopsis: This routine uses the other static constructor routines
// depending on the objectClass information available in the passed
// in ldapMsg. The state of the newly created object reflects this
// information. The newly created object is then prepopulated with
// the attributes in the ldapMsg. The return value from this routine
// is the newly created object (interface asked on this object).
//
// Arguments: Parent - Path to the parent of the newly created object.
// CommonName - Name of new object to create.
// Credentials - Standard credentials object.
// dwObjectState- Bound/unbound (always bound in this case).
// ldapHandle - Handle used in the deciphering search info.
// pldapMsg - The Ldao msg with the attributes.
// riid - IID requested on newly created object.
// ppvObj - Return value.
//
//
// Returns: S_OK, or any appropriate error code.
//
// Modifies: ppvObj contains newly created object with attributes.
//
//----------------------------------------------------------------------------
HRESULT
CLDAPGenObject::CreateGenericObject(
BSTR Parent,
BSTR CommonName,
CCredentials& Credentials,
DWORD dwObjectState,
PADSLDP ldapHandle,
LDAPMessage *pldapMsg,
REFIID riid,
void **ppvObj
)
{
HRESULT hr = S_OK;
TCHAR **aValues = NULL;
int nCount;
CLDAPGenObject *pGenObject = NULL;
hr = LdapGetValues(
ldapHandle,
pldapMsg,
L"objectClass",
&aValues,
&nCount
);
if (FAILED(hr) || !aValues) {
//
// We do not have the objectClass
//
hr = CLDAPGenObject::CreateGenericObject(
Parent,
CommonName,
L"Top",
Credentials,
dwObjectState,
riid,
(void **) &pGenObject,
TRUE, // class is defaulted
TRUE // No QI
);
}
else {
//
// We have the info we need for the constructor with class name
//
hr = CLDAPGenObject::CreateGenericObject(
Parent,
CommonName,
aValues,
nCount,
Credentials,
dwObjectState,
riid,
(void **) &pGenObject,
FALSE, // objectClass is not defaulted
TRUE // No QI
);
}
BAIL_ON_FAILURE(hr);
//
// Now we need to prepopulate the object.
//
hr = pGenObject->_pPropertyCache->
LDAPUnMarshallProperties2(
pGenObject->_pszLDAPServer,
pGenObject->_pLdapHandle,
pldapMsg,
FALSE, // this is not an explicit getinfo.
pGenObject->_Credentials,
&pGenObject->_fRangeRetrieval
);
//
// Should we really fail if we could not unmarshall properties ???
///
BAIL_ON_FAILURE(hr);
//
// This call is always from UMI, so now we need to get the
// outer umiObject and return that.
//
hr = ((CCoreADsObject*)pGenObject)->InitUmiObject(
IntfPropsGeneric,
pGenObject->_pPropertyCache,
(IADs *) pGenObject,
(IADs *) pGenObject,
riid,
ppvObj,
&(pGenObject->_Credentials),
pGenObject->_dwPort,
pGenObject->_pszLDAPServer,
pGenObject->_pszLDAPDn,
pGenObject->_pLdapHandle,
pGenObject->_pExtMgr
);
BAIL_ON_FAILURE(hr);
//
// Only thing remaining is to get a list of the attributes fetched
// into this object. That way we wont go on the wire for them. This
// can be done once the GetInfoEx related bug is fixed.
//
error :
if (pGenObject && FAILED(hr)) {
delete pGenObject;
*ppvObj = NULL;
}
//
// Need to free the Ldap values in all cases.
//
if (aValues) {
LdapValueFree(aValues);
aValues = NULL;
}
RRETURN(hr);
}
CLDAPGenObject::~CLDAPGenObject( )
{
//
// last to be created - first to be unloaded
//
delete _pExtMgr;
VariantClear(&_vFilter);
VariantClear(&_vHints);
if ( _pLdapHandle )
{
LdapCloseObject(_pLdapHandle);
_pLdapHandle = NULL;
}
if (_pszLDAPServer) {
FreeADsStr(_pszLDAPServer);
_pszLDAPServer = NULL;
}
if (_pszLDAPDn) {
FreeADsStr(_pszLDAPDn);
_pszLDAPDn = NULL;
}
delete _pDispMgr;
delete _pPropertyCache;
//
// Free the sort keys if applicable.
//
if (_SearchPref._pSortKeys) {
for (DWORD dwCtr = 0; dwCtr < _SearchPref._nSortKeys; dwCtr++) {
if (_SearchPref._pSortKeys[dwCtr].sk_attrtype)
FreeADsStr(_SearchPref._pSortKeys[dwCtr].sk_attrtype);
}
FreeADsMem(_SearchPref._pSortKeys);
}
//
// Free the VLV information if applicable
//
if (_SearchPref._pVLVInfo) {
if (_SearchPref._pVLVInfo->ldvlv_attrvalue) {
if (_SearchPref._pVLVInfo->ldvlv_attrvalue->bv_val) {
FreeADsMem(_SearchPref._pVLVInfo->ldvlv_attrvalue->bv_val);
}
FreeADsMem(_SearchPref._pVLVInfo->ldvlv_attrvalue);
}
if (_SearchPref._pVLVInfo->ldvlv_context) {
if (_SearchPref._pVLVInfo->ldvlv_context->bv_val) {
FreeADsMem(_SearchPref._pVLVInfo->ldvlv_context->bv_val);
}
FreeADsMem(_SearchPref._pVLVInfo->ldvlv_context);
}
FreeADsMem(_SearchPref._pVLVInfo);
}
//
// Free the attribute scoped query information if applicable
//
if (_SearchPref._pAttribScoped) {
FreeADsStr(_SearchPref._pAttribScoped);
}
}
STDMETHODIMP
CLDAPGenObject::QueryInterface(REFIID iid, LPVOID FAR* ppv)
{
HRESULT hr = S_OK;
if (ppv == NULL) {
RRETURN(E_POINTER);
}
if (IsEqualIID(iid, IID_IUnknown))
{
*ppv = (IADs FAR *) this;
}
else if (IsEqualIID(iid, IID_IADsContainer))
{
*ppv = (IADsContainer FAR *) this;
}
else if (IsEqualIID(iid, IID_IADs))
{
*ppv = (IADs FAR *) this;
}
else if (IsEqualIID(iid, IID_IDispatch))
{
*ppv = (IADs FAR *) this;
}
else if (IsEqualIID(iid, IID_IDirectoryObject))
{
*ppv = (IDirectoryObject FAR *) this;
}
else if (IsEqualIID(iid, IID_IDirectorySearch))
{
*ppv = (IDirectorySearch FAR *) this;
}
else if (IsEqualIID(iid, IID_IDirectorySchemaMgmt))
{
*ppv = (IDirectorySchemaMgmt FAR *) this;
}
else if (IsEqualIID(iid, IID_IADsPropertyList))
{
*ppv = (IADsPropertyList FAR *) this;
}
else if (IsEqualIID(iid, IID_IADsObjectOptions))
{
*ppv = (IADsObjectOptions FAR *) this;
}
else if (IsEqualIID(iid, IID_IADsDeleteOps))
{
*ppv = (IADsDeleteOps FAR *) this;
}
else if (IsEqualIID(iid, IID_ISupportErrorInfo))
{
*ppv = (ISupportErrorInfo FAR *) this;
}
else if (IsEqualIID(iid, IID_IADsObjOptPrivate))
{
*ppv = (IADsObjOptPrivate *) this;
}
else if (_pExtMgr)
{
RRETURN(hr = _pExtMgr->QueryInterface(iid, ppv));
}else {
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
/* ISupportErrorInfo method */
HRESULT
CLDAPGenObject::InterfaceSupportsErrorInfo(THIS_ REFIID riid)
{
if (IsEqualIID(riid, IID_IADs) ||
IsEqualIID(riid, IID_IADsPropertyList) ||
IsEqualIID(riid, IID_IADsContainer) ||
#if 0
IsEqualIID(riid, IID_IDirectoryObject) ||
IsEqualIID(riid, IID_IDirectorySearch) ||
IsEqualIID(riid, IID_IDirectorySchemaMgmt) ||
#endif
IsEqualIID(riid, IID_IADsObjectOptions)) {
RRETURN(S_OK);
}
else {
RRETURN(S_FALSE);
}
}
HRESULT
CLDAPGenObject::SetInfo()
{
HRESULT hr = S_OK;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
if (GetObjectState() == ADS_OBJECT_UNBOUND) {
hr = LDAPCreateObject();
BAIL_ON_FAILURE(hr);
//
// If the create succeded, set the object type to bound
//
SetObjectState(ADS_OBJECT_BOUND);
}else {
hr = LDAPSetObject();
BAIL_ON_FAILURE(hr);
}
error:
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CLDAPGenObject::LDAPSetObject()
{
HRESULT hr = S_OK;
LDAPModW **aMod = NULL;
BOOL fNTSecDes=FALSE;
DWORD dwSecDescType = ADSI_LDAPC_SECDESC_NONE;
BOOL fModifyDone = FALSE;
SECURITY_INFORMATION NewSeInfo=0;
SECURITY_INFORMATION SeInfo = _seInfo;
BYTE berValue[8];
memset(berValue, 0, 8);
berValue[0] = 0x30; // Start sequence tag
berValue[1] = 0x03; // Length in bytes of following
berValue[2] = 0x02; // Actual value this and next 2
berValue[3] = 0x01;
berValue[4] = (BYTE) ((ULONG)SeInfo);
LDAPControl SeInfoControl =
{
LDAP_SERVER_SD_FLAGS_OID_W,
{
5, (PCHAR) berValue
},
TRUE
};
LDAPControl ModifyControl =
{
LDAP_SERVER_PERMISSIVE_MODIFY_OID_W,
{
0, NULL
},
FALSE
};
PLDAPControl ServerControls[2] =
{
&SeInfoControl,
NULL
};
PLDAPControl ServerControlsOnlyModify[2] =
{
&ModifyControl,
NULL
};
PLDAPControl ServerControlsAll[3] =
{
&SeInfoControl,
&ModifyControl,
NULL
};
BOOL fServerIsAD = FALSE;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
hr = _pPropertyCache->LDAPMarshallProperties(
&aMod,
&fNTSecDes,
&NewSeInfo
);
BAIL_ON_FAILURE(hr);
if ( aMod == NULL ) // There are no changes that needs to be modified
RRETURN(S_OK);
//
// Sometimes the call to Marshall might contain the SD but NewSeInfo
// might not have been updated suitable because of failures not sever
// enough to warrant failing the entire operation.
//
if (!_fExplicitSecurityMask
&& fNTSecDes
&& (NewSeInfo != INVALID_SE_VALUE)
) {
berValue[4] = (BYTE) ((ULONG)NewSeInfo);
}
//
// Find out if server is AD.
//
hr = ReadServerSupportsIsADControl(
_pszLDAPServer,
&fServerIsAD,
_Credentials,
_dwPort
);
if (FAILED(hr)) {
//
// Assume it is not AD and continue, there is no
// good reason for this to fail on AD.
//
fServerIsAD = FALSE;
}
if (fNTSecDes) {
hr = ReadSecurityDescriptorControlType(
_pszLDAPServer,
&dwSecDescType,
_Credentials,
_dwPort
);
if (SUCCEEDED(hr) && (dwSecDescType == ADSI_LDAPC_SECDESC_NT)) {
hr = LdapModifyExtS(
_pLdapHandle,
_pszLDAPDn,
aMod,
fServerIsAD ?
(PLDAPControl *) &ServerControlsAll :
(PLDAPControl *) &ServerControls,
NULL
);
fModifyDone = TRUE;
} // SecDesc type == NT
} // if modifySecDes
if (!fModifyDone) {
if (fServerIsAD) {
//
// Need to send the additional control that says it is
// ok to clear the values on attributes that do not
// have any values.
//
hr = LdapModifyExtS(
_pLdapHandle,
_pszLDAPDn,
aMod,
(PLDAPControl *) &ServerControlsOnlyModify,
NULL
);
}
else {
hr = LdapModifyS(
_pLdapHandle,
_pszLDAPDn,
aMod
);
}
}
BAIL_ON_FAILURE(hr);
// We are successful at this point,
// So, clean up the flags in the cache so the same operation
// won't be repeated on the next SetInfo()
_pPropertyCache->ClearMarshalledProperties();
_pPropertyCache->ClearAllPropertyFlags();
_pPropertyCache->DeleteSavingEntry();
error:
if (aMod) {
if ( *aMod )
FreeADsMem( *aMod );
FreeADsMem( aMod );
}
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CLDAPGenObject::LDAPCreateObject()
{
HRESULT hr = S_OK;
LDAPModW **aMod = NULL;
DWORD dwIndex = 0;
BOOL fNTSecDes= FALSE;
BOOL fAddDone = FALSE;
DWORD dwSecDescType = ADSI_LDAPC_SECDESC_NONE;
SECURITY_INFORMATION SeInfo = _seInfo;
SECURITY_INFORMATION NewSeInfo=0;
BYTE berValue[8];
memset(berValue, 0, 8);
berValue[0] = 0x30; // Start sequence tag
berValue[1] = 0x03; // Length in bytes of following
berValue[2] = 0x02; // Actual value this and next 2
berValue[3] = 0x01;
berValue[4] = (BYTE) ((ULONG)SeInfo);
LDAPControl SeInfoControl =
{
LDAP_SERVER_SD_FLAGS_OID_W,
{
5, (PCHAR) berValue
},
TRUE
};
PLDAPControl ServerControls[2] =
{
&SeInfoControl,
NULL
};
if ( _pPropertyCache->findproperty( TEXT("objectClass"), &dwIndex )
== E_ADS_PROPERTY_NOT_FOUND )
{
VARIANT v;
VariantInit(&v);
v.vt = VT_BSTR;
V_BSTR(&v) = _SchemaClass;
hr = Put( TEXT("objectClass"), v );
BAIL_ON_FAILURE(hr);
}
hr = _pPropertyCache->LDAPMarshallProperties(
&aMod,
&fNTSecDes,
&NewSeInfo
);
BAIL_ON_FAILURE(hr);
if (!_fExplicitSecurityMask && NewSeInfo) {
berValue[4] = (BYTE) ((ULONG)NewSeInfo);
}
if (fNTSecDes == TRUE) {
//
// If we support the SD control, then we use
// the add ext method.
//
if (fNTSecDes) {
hr = ReadSecurityDescriptorControlType(
_pszLDAPServer,
&dwSecDescType,
_Credentials,
_dwPort
);
if (SUCCEEDED(hr) && (dwSecDescType == ADSI_LDAPC_SECDESC_NT)) {
hr = LdapAddExtS(
_pLdapHandle,
_pszLDAPDn,
aMod,
(PLDAPControl *)&ServerControls,
NULL
);
fAddDone = TRUE;
} // SecDesc type == NT
}
} // if SecDesc needs to be sent.
if (!fAddDone) {
//
// Call add s
//
hr = LdapAddS(
_pLdapHandle,
_pszLDAPDn,
aMod
);
}
BAIL_ON_FAILURE(hr);
// We are successful at this point,
// So, clean up the flags in the cache so the same operation
// won't be repeated on the next SetInfo()
_pPropertyCache->ClearAllPropertyFlags();
error:
if (aMod) {
if ( *aMod )
FreeADsMem( *aMod );
FreeADsMem( aMod );
}
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CLDAPGenObject::GetInfoEx(
THIS_ VARIANT vProperties,
long lnReserved
)
{
HRESULT hr = S_OK;
LDAPMessage *res = NULL;
VARIANT *vVarArray = NULL;
DWORD dwNumVariants = 0;
PWSTR *ppszStringArray = NULL;
DWORD dwOptions = 0;
int ldaperr = 0;
DWORD dwCtr = 0;
DWORD dwSecDescType = 0;
BOOL fSearchDone = FALSE;
SECURITY_INFORMATION SeInfo = _seInfo;
BYTE berValue[8];
memset(berValue, 0, 8);
berValue[0] = 0x30; // Start sequence tag
berValue[1] = 0x03; // Length in bytes of following
berValue[2] = 0x02; // Actual value this and next 2
berValue[3] = 0x01;
berValue[4] = (BYTE)((ULONG)SeInfo);
LDAPControl SeInfoControl =
{
LDAP_SERVER_SD_FLAGS_OID_W,
{
5, (PCHAR) berValue
},
TRUE
};
PLDAPControl ServerControls[2] =
{
&SeInfoControl,
NULL
};
UNREFERENCED_PARAMETER(lnReserved);
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
if (GetObjectState() == ADS_OBJECT_UNBOUND) {
hr = E_ADS_OBJECT_UNBOUND;
BAIL_ON_FAILURE(hr);
}
hr = ConvertSafeArrayToVariantArray(
vProperties,
&vVarArray,
&dwNumVariants
);
// returns E_FAIL if vProperties is invalid
if (hr == E_FAIL)
hr = E_ADS_BAD_PARAMETER;
BAIL_ON_FAILURE(hr);
hr = ConvertVariantArrayToLDAPStringArray(
vVarArray,
&ppszStringArray,
dwNumVariants
);
BAIL_ON_FAILURE(hr);
while ((dwCtr < dwNumVariants) && (dwSecDescType == 0)) {
if (_wcsicmp(ppszStringArray[dwCtr], L"ntSecurityDescriptor") == 0) {
dwSecDescType = 1;
}
dwCtr++;
}
//
// Do not bother doing this if secdesc has not been
// explicitly requested.
//
if (dwSecDescType) {
//
// If the server is V3, we want to use controls in case
// the security descriptor has to be retrieved
//
ldaperr = ldap_get_option(
_pLdapHandle->LdapHandle,
LDAP_OPT_VERSION,
&dwOptions
);
if (dwOptions == LDAP_VERSION3) {
hr = ReadSecurityDescriptorControlType(
_pszLDAPServer,
&dwSecDescType,
_Credentials,
_dwPort
);
BAIL_ON_FAILURE(hr);
if (dwSecDescType == ADSI_LDAPC_SECDESC_NT) {
hr = LdapSearchExtS(
_pLdapHandle,
_pszLDAPDn,
LDAP_SCOPE_BASE,
TEXT("(objectClass=*)"),
ppszStringArray,
0,
(PLDAPControl *)&ServerControls,
NULL,
NULL,
10000,
&res
);
fSearchDone = TRUE;
}
}
} // sec desc requested
//
// At this point even if it is a vesion 3 DS, we might still
// need to call the normal search routine as all version 3
// DS's need not necessarily support controls - Ex: Exchange.
//
if (!fSearchDone) {
hr = LdapSearchS(
_pLdapHandle,
_pszLDAPDn,
LDAP_SCOPE_BASE,
TEXT("(objectClass=*)"),
ppszStringArray,
0,
&res
);
fSearchDone = TRUE;
}
BAIL_ON_FAILURE(hr);
//
// This is an explicit GetInfo[Ex], but we don't want to flush the
// property cache. For example, if we have [A B C] in the cache, and
// we request [C D E], we want to end up with [A B C D E] in the cache.
//
// But we do want to tell LDAPUnMarshallProperties that this is an
// explicit call; using the same example, we want the server's value of
// C in the cache after this call, not the old value.
//
hr = _pPropertyCache->LDAPUnMarshallProperties2(
_pszLDAPServer,
_pLdapHandle,
res,
TRUE, // fExplicit
_Credentials,
&_fRangeRetrieval
);
BAIL_ON_FAILURE(hr);
for(DWORD i = 0; i < dwNumVariants; i++) {
_pPropertyCache->AddSavingEntry(ppszStringArray[i]);
}
error:
if (res)
LdapMsgFree(res);
if (ppszStringArray) {
for (DWORD i = 0; i < dwNumVariants; i++)
if (ppszStringArray[i])
FreeADsStr(ppszStringArray[i]);
FreeADsMem(ppszStringArray);
}
if (vVarArray) {
for (dwCtr = 0; dwCtr < dwNumVariants; dwCtr++) {
VariantClear(vVarArray + dwCtr);
}
FreeADsMem(vVarArray);
}
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CLDAPGenObject::GetInfo()
{
_fRangeRetrieval = FALSE;
RRETURN(GetInfo(GETINFO_FLAG_EXPLICIT));
}
HRESULT
CLDAPGenObject::GetInfo(
DWORD dwFlags
)
{
HRESULT hr = S_OK;
LDAPMessage *res = NULL;
int ldaperr = 0;
DWORD dwOptions = 0;
DWORD dwSecDescType = 0;
BOOL fSearchDone = FALSE;
SECURITY_INFORMATION SeInfo = _seInfo;
BYTE berValue[8];
memset(berValue, 0, 8);
berValue[0] = 0x30; // Start sequence tag
berValue[1] = 0x03; // Length in bytes of following
berValue[2] = 0x02; // Actual value this and next 2
berValue[3] = 0x01;
berValue[4] = (BYTE)((ULONG)SeInfo);
LDAPControl SeInfoControl =
{
LDAP_SERVER_SD_FLAGS_OID_W,
{
5, (PCHAR) berValue
},
TRUE
};
PLDAPControl ServerControls[2] =
{
&SeInfoControl,
NULL
};
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
if (dwFlags == GETINFO_FLAG_IMPLICIT_AS_NEEDED) {
if (_pPropertyCache->getGetInfoFlag()) {
//
// We are done there is nothing to do.
//
goto error;
}
}
if (GetObjectState() == ADS_OBJECT_UNBOUND) {
hr = E_ADS_OBJECT_UNBOUND;
BAIL_ON_FAILURE(hr);
}
if ( dwFlags == GETINFO_FLAG_EXPLICIT )
{
// If this is an explicit GetInfo,
// delete the old cache and start a new cache from scratch.
_pPropertyCache->flushpropertycache();
}
// modified from LdapSearchS to LdapSearchExtS to get all attributes
// including SecurityDescriptor by one call
ldaperr = ldap_get_option(
_pLdapHandle->LdapHandle,
LDAP_OPT_VERSION,
&dwOptions
);
if (dwOptions == LDAP_VERSION3) {
hr = ReadSecurityDescriptorControlType(
_pszLDAPServer,
&dwSecDescType,
_Credentials,
_dwPort
);
BAIL_ON_FAILURE(hr);
if (dwSecDescType == ADSI_LDAPC_SECDESC_NT) {
hr = LdapSearchExtS(
_pLdapHandle,
_pszLDAPDn,
LDAP_SCOPE_BASE,
TEXT("(objectClass=*)"),
NULL, // modified to NULL for all attributes
0,
(PLDAPControl *)&ServerControls,
NULL,
NULL,
10000,
&res
);
fSearchDone = TRUE;
}
}
//
// If the fSearchDone flags is not set, then the server
// probably did not support the SecDesc control and we need to
// just a search not extended.
//
if (!fSearchDone) {
hr = LdapSearchS(
_pLdapHandle,
_pszLDAPDn,
LDAP_SCOPE_BASE,
TEXT("(objectClass=*)"),
NULL,
0,
&res
);
fSearchDone = TRUE;
}
BAIL_ON_FAILURE(hr);
hr = _pPropertyCache->LDAPUnMarshallProperties2(
_pszLDAPServer,
_pLdapHandle,
res,
(dwFlags == GETINFO_FLAG_EXPLICIT) ?
TRUE : FALSE,
_Credentials,
&_fRangeRetrieval
);
BAIL_ON_FAILURE(hr);
_pPropertyCache->setGetInfoFlag();
error:
if (res) {
LdapMsgFree( res );
}
if (_pPropertyCache) {
Reset();
}
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::get_GUID(THIS_ BSTR FAR* retval)
{
HRESULT hr = S_OK;
DWORD dwSyntaxId;
DWORD dwStatus = 0;
ULONG ulLength = 0;
LDAPOBJECTARRAY ldapSrcObjects;
LPWSTR pszTempStr = NULL;
WCHAR pszSmallStr[5];
LDAPOBJECTARRAY_INIT(ldapSrcObjects);
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
if (_dwObjectState == ADS_OBJECT_UNBOUND) {
RRETURN(E_ADS_OBJECT_UNBOUND);
}
if (!(_dwCorePropStatus & LDAP_GUID_VALID)) {
//
// Get the property from the server (implicit getinfo)
// and update the property in the
//
hr = _pPropertyCache->getproperty(
L"objectGUID",
&dwSyntaxId,
&dwStatus,
&ldapSrcObjects
);
BAIL_ON_FAILURE(hr);
if ((ldapSrcObjects.dwCount == 0)
|| (ldapSrcObjects.dwCount > 1)) {
BAIL_ON_FAILURE(hr = E_ADS_PROPERTY_NOT_FOUND);
}
ulLength = LDAPOBJECT_BERVAL_LEN(ldapSrcObjects.pLdapObjects);
pszTempStr = (LPWSTR) AllocADsMem((ulLength * 2 + 1) * sizeof(WCHAR));
if (!pszTempStr) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
for (DWORD dwCtr = 0; dwCtr < ulLength; dwCtr++) {
wsprintf(pszSmallStr, L"%02x", (BYTE) LDAPOBJECT_BERVAL_VAL(ldapSrcObjects.pLdapObjects)[dwCtr]);
wcscat(pszTempStr, pszSmallStr);
}
wcscat(pszTempStr, L"\0");
if (_ADsGuid) {
ADsFreeString(_ADsGuid);
_ADsGuid = NULL;
}
hr = ADsAllocString(pszTempStr, &_ADsGuid);
if (SUCCEEDED(hr)) {
_dwCorePropStatus |= LDAP_GUID_VALID;
}
}
error:
LdapTypeFreeLdapObjects( &ldapSrcObjects );
if (pszTempStr) {
FreeADsMem(pszTempStr);
}
if (SUCCEEDED(hr)) {
RRETURN(get_CoreGUID(retval));
}
RRETURN(hr);
}
HRESULT
CLDAPGenObject::get_Parent(BSTR * retval)
{
HRESULT hr;
DWORD dwSyntaxId;
DWORD dwStatus = 0;
LDAPOBJECTARRAY ldapSrcObjects;
LPWSTR pszTempStr = NULL;
LPWSTR pszADsPath = NULL;
LPWSTR pszADsParent = NULL;
LPWSTR pszADsCommon = NULL;
BOOL fGCNameSpace = FALSE;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
LDAPOBJECTARRAY_INIT(ldapSrcObjects);
if (FAILED(hr = ValidateOutParameter(retval))){
RRETURN_EXP_IF_ERR(hr);
}
if ( (wcslen(_Name) > wcslen(L"<GUID=")) &&
(_wcsnicmp(_Name, L"<GUID=", wcslen(L"<GUID=")) == 0)) {
//
// Replace the guid with the distinguishedName
//
//
// Get the property from the server (implicit getinfo)
//
hr = _pPropertyCache->getproperty(
L"distinguishedName",
&dwSyntaxId,
&dwStatus,
&ldapSrcObjects
);
BAIL_ON_FAILURE(hr);
if (ldapSrcObjects.dwCount == 0) {
BAIL_ON_FAILURE(hr = E_ADS_PROPERTY_NOT_FOUND);
}
pszTempStr = LDAPOBJECT_STRING(ldapSrcObjects.pLdapObjects);
//
// Break it down into parent name & common name portions
//
// _ADsPath could not be NULL
if(!_wcsnicmp(L"GC:", _ADsPath, wcslen(L"GC:")))
{
fGCNameSpace = TRUE;
}
hr = BuildADsPathFromLDAPPath2(
(_pszLDAPServer ? TRUE : FALSE),
fGCNameSpace ? L"GC:" : L"LDAP:",
_pszLDAPServer,
_dwPort,
pszTempStr,
&pszADsPath
);
BAIL_ON_FAILURE(hr);
hr = BuildADsParentPath(pszADsPath,
&pszADsParent,
&pszADsCommon);
BAIL_ON_FAILURE(hr);
hr = ADsAllocString(pszADsParent, retval);
}
else {
hr = get_CoreParent(retval);
}
error:
LdapTypeFreeLdapObjects( &ldapSrcObjects );
if (pszADsPath)
FreeADsMem(pszADsPath);
if (pszADsParent)
FreeADsMem(pszADsParent);
if (pszADsCommon)
FreeADsMem(pszADsCommon);
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CLDAPGenObject::get_Name(BSTR * retval)
{
HRESULT hr;
DWORD dwSyntaxId;
DWORD dwStatus = 0;
LDAPOBJECTARRAY ldapSrcObjects;
LPWSTR pszTempStr = NULL;
LPWSTR pszADsPath = NULL;
LPWSTR pszADsParent = NULL;
LPWSTR pszADsCommon = NULL;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
LDAPOBJECTARRAY_INIT(ldapSrcObjects);
if (FAILED(hr = ValidateOutParameter(retval))){
RRETURN_EXP_IF_ERR(hr);
}
if ( (wcslen(_Name) > wcslen(L"<WKGUID=")) &&
(_wcsnicmp(_Name, L"<WKGUID=", wcslen(L"<WKGUID=")) == 0)) {
//
// Replace the name with the DN
//
hr = ADsAllocString(_pszLDAPDn, retval);
}
else if ( (wcslen(_Name) > wcslen(L"<GUID=")) &&
(_wcsnicmp(_Name, L"<GUID=", wcslen(L"<GUID=")) == 0)) {
//
// Replace the guid with the distinguishedName
//
//
// Get the property from the server (implicit getinfo)
//
hr = _pPropertyCache->getproperty(
L"distinguishedName",
&dwSyntaxId,
&dwStatus,
&ldapSrcObjects
);
BAIL_ON_FAILURE(hr);
if (ldapSrcObjects.dwCount == 0) {
BAIL_ON_FAILURE(hr = E_ADS_PROPERTY_NOT_FOUND);
}
pszTempStr = LDAPOBJECT_STRING(ldapSrcObjects.pLdapObjects);
//
// Break it down into parent name & common name portions
//
hr = BuildADsPathFromLDAPPath2(
(_pszLDAPServer ? TRUE : FALSE),
L"LDAP:", // does not matter, just a place holder
_pszLDAPServer,
_dwPort, // doesn't matter
pszTempStr,
&pszADsPath
);
BAIL_ON_FAILURE(hr);
hr = BuildADsParentPath(pszADsPath,
&pszADsParent,
&pszADsCommon);
BAIL_ON_FAILURE(hr);
hr = ADsAllocString(pszADsCommon, retval);
}
else {
hr = ADsAllocString(_Name, retval);
}
error:
LdapTypeFreeLdapObjects( &ldapSrcObjects );
if (pszADsPath)
FreeADsMem(pszADsPath);
if (pszADsParent)
FreeADsMem(pszADsParent);
if (pszADsCommon)
FreeADsMem(pszADsCommon);
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::get_Class(THIS_ BSTR FAR* retval)
{
HRESULT hr = S_OK;
DWORD dwSyntaxId;
DWORD dwStatus = 0;
ULONG ulNumVals = 0;
LDAPOBJECTARRAY ldapSrcObjects;
PLDAPOBJECT pLdapObject = NULL;
LPWSTR pszTempStr = NULL;
LDAPOBJECTARRAY_INIT(ldapSrcObjects);
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
//
// Need to go on wire only if object is bound as in this
// object is not being create now
//
if (!(_dwCorePropStatus & LDAP_CLASS_VALID)
&& _dwObjectState != ADS_OBJECT_UNBOUND) {
//
// Get the property from the server (implicit getinfo)
// and update the property in the
//
hr = _pPropertyCache->getproperty(
L"objectClass",
&dwSyntaxId,
&dwStatus,
&ldapSrcObjects
);
BAIL_ON_FAILURE(hr);
if (ldapSrcObjects.dwCount == 0) {
BAIL_ON_FAILURE(hr = E_ADS_PROPERTY_NOT_FOUND);
}
ulNumVals = ldapSrcObjects.dwCount;
pLdapObject = ldapSrcObjects.pLdapObjects;
//
// Try and see if we need the first or the last value
//
if (_wcsicmp(LDAPOBJECT_STRING(pLdapObject + (ulNumVals - 1)),
L"Top")== 0) {
pszTempStr = LDAPOBJECT_STRING(
pLdapObject + 0
);
} else {
pszTempStr = LDAPOBJECT_STRING(
pLdapObject + (ulNumVals - 1)
);
}
if (_SchemaClass) {
ADsFreeString(_SchemaClass);
_SchemaClass = NULL;
}
hr = ADsAllocString(pszTempStr, &_SchemaClass);
if (SUCCEEDED(hr)) {
_dwCorePropStatus |= LDAP_CLASS_VALID;
}
}
error:
LdapTypeFreeLdapObjects( &ldapSrcObjects );
if (SUCCEEDED(hr)) {
RRETURN(get_CoreADsClass(retval));
}
RRETURN(hr);
}
STDMETHODIMP
CLDAPGenObject::get_Schema(THIS_ BSTR FAR* retval)
{
BSTR bstrTemp = NULL;
//
// We call the get_Class method because that will take care
// of all the work we need to do in the event that we need
// read the information from the server. It makes sense to do
// that rather than repeat the code here.
//
HRESULT hr = get_Class(&bstrTemp);
if (FAILED(hr)) {
RRETURN(hr);
}
if (bstrTemp) {
SysFreeString(bstrTemp);
}
RRETURN(get_CoreSchema(retval));
}
/* IADsContainer methods */
STDMETHODIMP
CLDAPGenObject::get_Count(long FAR* retval)
{
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
RRETURN_EXP_IF_ERR(E_NOTIMPL);
}
STDMETHODIMP
CLDAPGenObject::get_Filter(THIS_ VARIANT FAR* pVar)
{
HRESULT hr;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
VariantInit(pVar);
hr = VariantCopy(pVar, &_vFilter);
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::put_Filter(THIS_ VARIANT Var)
{
HRESULT hr;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
VariantClear(&_vFilter);
hr = VariantCopy(&_vFilter, &Var);
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::get_Hints(THIS_ VARIANT FAR* pVar)
{
HRESULT hr;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
VariantInit(pVar);
hr = VariantCopy(pVar, &_vHints);
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::put_Hints(THIS_ VARIANT Var)
{
HRESULT hr;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
VariantClear(&_vHints);
hr = VariantCopy(&_vHints, &Var);
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::GetObject(
THIS_ BSTR ClassName,
BSTR RelativeName,
IDispatch * FAR* ppObject
)
{
TCHAR *pszBuffer = NULL;
HRESULT hr = S_OK;
IADs *pADs = NULL;
BSTR bstrClass = NULL;
BSTR bstrParent = NULL;
BSTR bstrName = NULL;
LPWSTR pszADsPath = NULL;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
if (!RelativeName || !*RelativeName) {
RRETURN_EXP_IF_ERR(E_ADS_UNKNOWN_OBJECT);
}
//
// Build an ADsPath from get_Parent/get_Name
// (which always returns useful LDAP-style values)
// rather than using _ADsPath, which could be
// of the <GUID=....> form
//
hr = get_Parent(&bstrParent);
BAIL_ON_FAILURE(hr);
hr = get_Name(&bstrName);
BAIL_ON_FAILURE(hr);
hr = BuildADsPathFromParent(bstrParent,
bstrName,
&pszADsPath);
BAIL_ON_FAILURE(hr);
//
// Tack on the path component of the child
// object being retrieved
//
hr = BuildADsPathFromParent(
pszADsPath,
RelativeName,
&pszBuffer
);
BAIL_ON_FAILURE(hr);
hr = ::GetObject(
pszBuffer,
_Credentials,
(LPVOID *)ppObject
);
BAIL_ON_FAILURE(hr);
//
// Check the class name only if we are not in umi land. In umi
// land, we will fail the QI for the IID_IADs interface.
//
if(ClassName && !(_Credentials.GetAuthFlags() & ADS_AUTH_RESERVED)) {
hr = (*ppObject)->QueryInterface(
IID_IADs,
(void **)&pADs
);
BAIL_ON_FAILURE(hr);
hr = pADs->get_Class(&bstrClass);
BAIL_ON_FAILURE(hr);
#ifdef WIN95
if (_wcsicmp( bstrClass, ClassName )) {
#else
if (CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
bstrClass,
-1,
ClassName,
-1
) != CSTR_EQUAL) {
#endif
(*ppObject)->Release();
*ppObject = NULL;
BAIL_ON_FAILURE(hr=E_ADS_UNKNOWN_OBJECT);
}
}
error:
if (bstrParent) {
ADsFreeString(bstrParent);
}
if (bstrName) {
ADsFreeString(bstrName);
}
if (pszADsPath) {
FreeADsMem(pszADsPath);
}
if ( pADs ) {
pADs->Release();
}
if ( pszBuffer )
FreeADsMem( pszBuffer );
if ( bstrClass ) {
SysFreeString( bstrClass );
}
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::get__NewEnum(
THIS_ IUnknown * FAR* retval
)
{
HRESULT hr = S_OK;
IUnknown FAR* punkEnum=NULL;
IEnumVARIANT * penum = NULL;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
*retval = NULL;
hr = CLDAPGenObjectEnum::Create(
(CLDAPGenObjectEnum **)&penum,
_ADsPath,
_pLdapHandle,
this,
_vFilter,
_Credentials,
_dwOptReferral,
_dwPageSize
);
BAIL_ON_FAILURE(hr);
hr = penum->QueryInterface(
IID_IUnknown,
(VOID FAR* FAR*)retval
);
BAIL_ON_FAILURE(hr);
if (penum) {
penum->Release();
}
RRETURN(NOERROR);
error:
if (penum) {
delete penum;
}
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::Create(
THIS_ BSTR ClassName,
BSTR RelativeName,
IDispatch * FAR* ppObject
)
{
HRESULT hr = S_OK;
BOOL fValid = FALSE;
BSTR bstrParent = NULL;
BSTR bstrName = NULL;
LPWSTR pszADsPath = NULL;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
//
// No null or empty names for class or rdn.
//
if (!ClassName || !*ClassName
|| !RelativeName || !*RelativeName) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
//
// Build an ADsPath from get_Parent/get_Name
// (which always returns useful LDAP-style values)
// rather than using _ADsPath, which could be
// of the <GUID=....> form
//
hr = get_Parent(&bstrParent);
BAIL_ON_FAILURE(hr);
hr = get_Name(&bstrName);
BAIL_ON_FAILURE(hr);
hr = BuildADsPathFromParent(bstrParent,
bstrName,
&pszADsPath);
BAIL_ON_FAILURE(hr);
hr = CLDAPGenObject::CreateGenericObject(
pszADsPath,
RelativeName,
ClassName,
_Credentials,
ADS_OBJECT_UNBOUND,
IID_IADs,
(void **) ppObject
);
BAIL_ON_FAILURE(hr);
error:
if (bstrParent) {
ADsFreeString(bstrParent);
}
if (bstrName) {
ADsFreeString(bstrName);
}
if (pszADsPath) {
FreeADsMem(pszADsPath);
}
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::Delete(
THIS_ BSTR bstrClassName,
BSTR bstrRelativeName
)
{
HRESULT hr = S_OK;
BSTR bstrParent = NULL;
BSTR bstrName = NULL;
LPWSTR pszADsPath = NULL;
BSTR bstrAbsoluteName = NULL;
TCHAR *pszLDAPServer = NULL;
TCHAR *pszLDAPDn = NULL;
LPTSTR *aValues = NULL;
int nCount = 0;
DWORD dwPort = 0;
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
//
// Check to see if the RelativeName is non empty
//
if (bstrRelativeName == NULL) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
//
// Build an ADsPath from get_Parent/get_Name
// (which always returns useful LDAP-style values)
// rather than using _ADsPath, which could be
// of the <GUID=....> form
//
hr = get_Parent(&bstrParent);
BAIL_ON_FAILURE(hr);
hr = get_Name(&bstrName);
BAIL_ON_FAILURE(hr);
hr = BuildADsPathFromParent(bstrParent,
bstrName,
&pszADsPath);
BAIL_ON_FAILURE(hr);
//
// Tack on the path component of the child object to be
// deleted
//
hr = BuildADsPath(
pszADsPath,
bstrRelativeName,
&bstrAbsoluteName
);
BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2(
bstrAbsoluteName,
&pszLDAPServer,
&pszLDAPDn,
&dwPort
);
BAIL_ON_FAILURE(hr);
//
// Compare the class names only if one is given. Null
// can be used to speed up the delete operation.
//
if (bstrClassName) {
//
// Validate the class name first
//
hr = LdapReadAttribute(
pszLDAPServer,
pszLDAPDn,
TEXT("objectClass"),
&aValues,
&nCount,
_Credentials,
_dwPort
);
BAIL_ON_FAILURE(hr);
if ( nCount > 0 )
{
if ( _tcsicmp( bstrClassName, GET_BASE_CLASS( aValues, nCount) ) != 0 )
{
hr = E_ADS_BAD_PARAMETER;
BAIL_ON_FAILURE(hr);
}
}
}
hr = LdapDeleteS(
_pLdapHandle,
pszLDAPDn
);
BAIL_ON_FAILURE(hr);
error:
if (bstrParent) {
ADsFreeString(bstrParent);
}
if (bstrName) {
ADsFreeString(bstrName);
}
if (pszADsPath) {
FreeADsMem(pszADsPath);
}
if ( bstrAbsoluteName ) {
ADsFreeString( bstrAbsoluteName );
}
if ( pszLDAPServer ) {
FreeADsStr( pszLDAPServer );
}
if (pszLDAPDn) {
FreeADsStr(pszLDAPDn);
}
if ( aValues ) {
LdapValueFree( aValues );
}
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::CopyHere(
THIS_ BSTR SourceName,
BSTR NewName,
IDispatch * FAR* ppObject
)
{
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
/*
RRETURN(CopyObject( SourceName,
_ADsPath,
_pLdapHandle,
_pSchemaInfo,
NewName,
ppObject ) );
*/
RRETURN_EXP_IF_ERR(E_NOTIMPL);
}
STDMETHODIMP
CLDAPGenObject::MoveHere(
THIS_ BSTR SourceName,
BSTR NewName,
IDispatch * FAR* ppObject
)
{
LPWSTR pszLDAPServer= NULL;
LPWSTR pszLDAPDn = NULL;
BSTR bstrParent = NULL;
BSTR bstrName = NULL;
LPWSTR pszADsPath = NULL;
HRESULT hr = S_OK;
LPWSTR pszSrcParent= NULL;
LPWSTR pszCN = NULL;
LPWSTR pszRelativeName = NULL;
BSTR bstrDestADsPath = NULL;
LPWSTR pszDestLDAPServer = NULL;
LPWSTR pszDestLDAPDn = NULL;
LPWSTR pszLDAPServerTemp = NULL;
LPWSTR pszLDAPSourceDnParent = NULL;
DWORD dwPort = 0;
LPBOOL lpBoolVal = NULL;
// Variables need to conver wide char to ANSI format
DWORD dwOptions = 0;
int intErr = 0;
LPSTR pANSIServer = NULL;
DWORD dwDestLen = 0;
PADSLDP pSourceLD = NULL;
BOOL fTryXDom = FALSE;
LPWSTR pszDestLDAPRelativeName = NULL;
LDAPControl RenExtInfoControl =
{
LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID_W,
{
0, NULL
},
TRUE
};
PLDAPControl ServerControls[2] =
{
&RenExtInfoControl,
NULL
};
//
// Make sure that the last error is reset
//
Macro_ClearADsLastError(L"LDAP Provider");
//
// Build the source paths of the object
// being moved
//
hr = BuildADsParentPath(
SourceName,
&pszSrcParent,
&pszCN
);
BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2(
SourceName,
&pszLDAPServer,
&pszLDAPDn,
&dwPort
);
BAIL_ON_FAILURE(hr);
if (!pszSrcParent) {
//
// If we cannot get the parent, then we cannot move this object.
//
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
//
// We need the dn to the parent of the object being moved.
//
hr = BuildLDAPPathFromADsPath2(
pszSrcParent,
&pszLDAPServerTemp,
&pszLDAPSourceDnParent,
&dwPort
);
BAIL_ON_FAILURE(hr);
//
// set the right value of relative distinguished name
// use the name given by the user if given at all
// otherwise use the name of the source
//
if (NewName != NULL) {
pszRelativeName = NewName;
} else {
pszRelativeName = pszCN;
}
//
// Build an ADsPath from get_Parent/get_Name
// (which always returns useful LDAP-style values)
// rather than using _ADsPath, which could be
// of the <GUID=....> form
//
hr = get_Parent(&bstrParent);
BAIL_ON_FAILURE(hr);
hr = get_Name(&bstrName);
BAIL_ON_FAILURE(hr);
hr = BuildADsPathFromParent(
bstrParent,
bstrName,
&pszADsPath
);
BAIL_ON_FAILURE(hr);
//
// Build the destination ADsPath of the object being moved.
//
hr = BuildADsPath(
pszADsPath,
pszRelativeName,
&bstrDestADsPath
);
BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2(
bstrDestADsPath,
&pszDestLDAPServer,
&pszDestLDAPDn,
&dwPort
);
BAIL_ON_FAILURE(hr);
//
// ADSI needs to escape /, but on server side, / is not a special character. So if someone passes
// in rdn but has ADSI type escaping, we need to convert it to ldap type path, otherwise server will
// reject this kind of rdn
//
hr = GetLDAPTypeName(
pszRelativeName,
&pszDestLDAPRelativeName
);
BAIL_ON_FAILURE(hr);
//
// LdapModDNS uses ldap_modrdn2_s. This function is used to
// rename the object not to really move it. If the path of this
// container and the parentDN of the object being moved here are
// not the same, we need to use ldapRenameExt instead.
//
#ifdef WIN95
if (!_wcsicmp(_pszLDAPDN, pszLDAPSourceDnParent)) {
#else
if (CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
_pszLDAPDn,
-1,
pszLDAPSourceDnParent,
-1
) == CSTR_EQUAL ) {
#endif
//
// They have the same parent, so we can use LdapModDnS.
//
hr = LdapModDnS(
_pLdapHandle,
pszLDAPDn,
pszDestLDAPRelativeName,
TRUE
);
}
else {
//
// Since the object is not in this container, we need to use
// the renameExt call.
//
hr = LdapRenameExtS(
_pLdapHandle,
pszLDAPDn,
pszDestLDAPRelativeName,
_pszLDAPDn,
TRUE,
NULL,
NULL
);
}
// if there was an error it maybe because the move should
// be across the domains.
if (FAILED(hr)) {
intErr = ldap_get_option(
_pLdapHandle->LdapHandle,
LDAP_OPT_VERSION,
&dwOptions
);
//
// Only if server is V3 and if the server names are not
// the same will we attempt the extended rename operation.
// is there are a better way to decide ?
//
if (!pszDestLDAPServer) {
//
// This object does not have a server, we should
// read it and then use that as the target server.
//
// If this call succeeds, it will copy the servername
// to the ptr, even if it fails, there should not be problem
// of memory leak
//
GetActualHostName(&pszDestLDAPServer);
} // if !pszDestLDAPServer
//
// Try the XDom move only if we have no match on the server
// names. Note that there is small chance that we will call
// crossdom when both are serverless but that should be rare
// as the ModDN call should have succeeded.
// If the source server is NULL, LDAPOpenObject will handle
// that case. The target can never be NULL for XDOm.
//
if (dwOptions == LDAP_VERSION3 && pszDestLDAPServer) {
if (!pszLDAPServer) {
//
// One server is set the other is not.
//
fTryXDom = TRUE;
}
#ifdef WIN95
else if (_wcsicmp(pszLDAPServer, pszDestLDAPServer)) {
#else
else if (CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
pszLDAPServer,
-1,
pszDestLDAPServer,
-1
) != CSTR_EQUAL ) {
#endif
//
// Only if both the servers are different.
//
fTryXDom = TRUE;
}
}
if (fTryXDom) {
//
// The move request has to go to the server that has
// the object not the target server.
//
dwDestLen = _tcslen(pszDestLDAPServer);
pANSIServer = (LPSTR)LocalAlloc(LPTR, dwDestLen * 2);
if (!pANSIServer) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
intErr = LdapUnicodeToUTF8(
pszDestLDAPServer,
dwDestLen,
pANSIServer,
dwDestLen * 2
);
/*
intErr = WideCharToMultiByte(
CP_OEMCP,
WC_DEFAULTCHAR,
pszDestLDAPServer,
dwDestLen,
pANSIDestServer,
dwDestLen * 2,
NULL,
lpBoolVal
);
*/
if (!intErr) {
// note that there has to be a valid hr from the
// previous LdapModDN call to be here.
BAIL_ON_FAILURE(hr);
}
// Update the control information
RenExtInfoControl.ldctl_value.bv_len = strlen(pANSIServer);
RenExtInfoControl.ldctl_value.bv_val = pANSIServer;
CCredentials Credentials = _Credentials;
//
// Add request delegation flag
//
Credentials.SetAuthFlags(
_Credentials.GetAuthFlags()
| ADS_USE_DELEGATION
);
hr = LdapOpenObject(
pszLDAPServer,
pszLDAPDn,
&pSourceLD,
Credentials,
dwPort
);
BAIL_ON_FAILURE(hr);
hr = LdapRenameExtS(
pSourceLD,
pszLDAPDn,
pszDestLDAPRelativeName,
_pszLDAPDn,
TRUE,
ServerControls,
NULL
);
}
}
BAIL_ON_FAILURE(hr);
//
// We do not need to pass the class name - even if we do it
// is not used
//
hr = GetObject(
NULL,
pszDestLDAPRelativeName,
(IDispatch **)ppObject
);
error:
if (bstrParent) {
ADsFreeString(bstrParent);
}
if (bstrName) {
ADsFreeString(bstrName);
}
if (pszADsPath) {
FreeADsMem(pszADsPath);
}
if(pszSrcParent){
FreeADsStr(pszSrcParent);
}
if(pszCN){
FreeADsStr(pszCN);
}
if(bstrDestADsPath){
ADsFreeString(bstrDestADsPath );
}
if(pszDestLDAPServer){
FreeADsStr(pszDestLDAPServer);
}
if(pszDestLDAPDn){
FreeADsStr(pszDestLDAPDn);
}
if(pszLDAPServer){
FreeADsStr(pszLDAPServer);
}
if(pszLDAPDn){
FreeADsStr(pszLDAPDn);
}
if (pszLDAPServerTemp) {
FreeADsStr(pszLDAPServerTemp);
}
if (pszLDAPSourceDnParent) {
FreeADsStr(pszLDAPSourceDnParent);
}
if (pANSIServer) {
LocalFree(pANSIServer);
}
if (pSourceLD) {
LdapCloseObject(pSourceLD);
}
if (pszDestLDAPRelativeName) {
FreeADsStr(pszDestLDAPRelativeName);
}
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CLDAPGenObject::AllocateGenObject(
LPWSTR pszClassName,
CCredentials &Credentials,
CLDAPGenObject ** ppGenObject
)
{
CLDAPGenObject FAR * pGenObject = NULL;
CAggregatorDispMgr FAR * pDispMgr = NULL;
CPropertyCache FAR * pPropertyCache = NULL;
HRESULT hr = S_OK;
pGenObject = new CLDAPGenObject();
if (pGenObject == NULL) {
hr = E_OUTOFMEMORY;
}
BAIL_ON_FAILURE(hr);
pDispMgr = new CAggregatorDispMgr(Credentials);
if (pDispMgr == NULL) {
hr = E_OUTOFMEMORY;
}
BAIL_ON_FAILURE(hr);
hr = pDispMgr->LoadTypeInfoEntry(
LIBID_ADs,
IID_IADs,
(IADs *)pGenObject,
DISPID_REGULAR
);
BAIL_ON_FAILURE(hr);
hr = pDispMgr->LoadTypeInfoEntry(
LIBID_ADs,
IID_IADsContainer,
(IADsContainer *)pGenObject,
DISPID_NEWENUM
);
BAIL_ON_FAILURE(hr);
hr = pDispMgr->LoadTypeInfoEntry(
LIBID_ADs,
IID_IADsPropertyList,
(IADsPropertyList *)pGenObject,
DISPID_VALUE
);
BAIL_ON_FAILURE(hr);
hr = pDispMgr->LoadTypeInfoEntry(
LIBID_ADs,
IID_IADsObjectOptions,
(IADsObjectOptions *)pGenObject,
DISPID_REGULAR
);
BAIL_ON_FAILURE(hr);
hr = pDispMgr->LoadTypeInfoEntry(
LIBID_ADs,
IID_IADsDeleteOps,
(IADsDeleteOps *)pGenObject,
DISPID_REGULAR
);
BAIL_ON_FAILURE(hr);
hr = CPropertyCache::createpropertycache(
(CCoreADsObject FAR *) pGenObject,
(IGetAttributeSyntax *)pGenObject,
&pPropertyCache
);
BAIL_ON_FAILURE(hr);
pDispMgr->RegisterPropertyCache(pPropertyCache);
pGenObject->_pPropertyCache = pPropertyCache;
pGenObject->_pDispMgr = pDispMgr;
*ppGenObject = pGenObject;
RRETURN(hr);
error:
delete pPropertyCache;
delete pDispMgr;
delete pGenObject;
RRETURN_EXP_IF_ERR(hr);
}
//
// IADsObjOptPrivate methods, IADsObjectOptions are wrapped
// around this.
//
STDMETHODIMP
CLDAPGenObject::GetOption(
DWORD dwOption,
void *pValue
)
{
HRESULT hr = S_OK;
CtxtHandle hCtxtHandle;
DWORD dwErr = 0;
ULONG ulFlags = 0;
if (!pValue) {
RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER);
}
switch( dwOption ) {
case LDP_CACHE_ENTRY:
*((PADSLDP *) pValue) = _pLdapHandle;
break;
case LDAP_HANDLE:
*((LDAP **) pValue) = _pLdapHandle ? _pLdapHandle->LdapHandle : (LDAP *) NULL;
break;
case LDAP_SERVER:
//
// pValue here is expected to be a pointer to LPWSTR
//
hr = GetActualHostName((LPWSTR *)pValue);
break;
case LDAP_DN:
*((LPWSTR *) pValue) = _pszLDAPDn;
break;
case LDAP_CHASE_REFERRALS:
*((DWORD *) pValue) = _dwOptReferral;
break;
case LDAP_PAGESIZE:
*((DWORD *) pValue) = _dwPageSize;
break;
case LDAP_SECURITY_MASK:
*((SECURITY_INFORMATION*) pValue) = _seInfo;
break;
case LDAP_MUTUAL_AUTH_STATUS:
dwErr = ldap_get_option(
_pLdapHandle->LdapHandle,
LDAP_OPT_SECURITY_CONTEXT,
(void *) &hCtxtHandle
);
if (dwErr) {
BAIL_ON_FAILURE(hr = E_FAIL);
}
#if (!defined(WIN95))
dwErr = QueryContextAttributesWrapper(
&hCtxtHandle,
SECPKG_ATTR_FLAGS,
(void *) &ulFlags
);
if (dwErr) {
if (dwErr == SEC_E_INVALID_HANDLE) {
//
// This will happen when SSL is used for certain.
//
hr = E_ADS_BAD_PARAMETER;
}
else {
hr = HRESULT_FROM_WIN32(dwErr);
}
BAIL_ON_FAILURE(hr);
}
#else
ulFlags = 0;
#endif
*((ULONG *) pValue) = ulFlags;
break;
case LDAP_MEMBER_HAS_RANGE :
*((BOOL *) pValue) = _fRangeRetrieval;
break;
default:
*((DWORD *) pValue) = 0;
hr = E_ADS_BAD_PARAMETER;
}
error:
RRETURN_EXP_IF_ERR(hr);
}
STDMETHODIMP
CLDAPGenObject::SetOption(
DWORD dwOption,
void *pValue
)
{
HRESULT hr = S_OK;
int ldaperr = 0;
void *ldapOption = 0;
switch (dwOption) {
case LDAP_CHASE_REFERRALS:
switch (*((DWORD *)pValue) ) {
case ADS_CHASE_REFERRALS_NEVER:
_dwOptReferral = (DWORD) (DWORD_PTR) LDAP_OPT_OFF;
break;
case ADS_CHASE_REFERRALS_SUBORDINATE:
_dwOptReferral = LDAP_CHASE_SUBORDINATE_REFERRALS;
break;
case ADS_CHASE_REFERRALS_EXTERNAL:
_dwOptReferral = LDAP_CHASE_EXTERNAL_REFERRALS;
break;
case ADS_CHASE_REFERRALS_ALWAYS:
_dwOptReferral = (DWORD) (DWORD_PTR) LDAP_OPT_ON;
break;
default:
RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER);
}
break;
case LDAP_PAGESIZE:
_dwPageSize = *((DWORD *)pValue);
break;
case LDAP_SECURITY_MASK:
_seInfo = *((SECURITY_INFORMATION *) pValue);
_fExplicitSecurityMask = TRUE;
break;
default:
hr = E_ADS_BAD_PARAMETER;
break;
}
RRETURN_EXP_IF_ERR( hr );
}
//
// IADsObjecOptions methods - wrapper around IADsObjOptPrivate
//
STDMETHODIMP
CLDAPGenObject::GetOption(
THIS_ long lnControlCode,
VARIANT FAR* pvProp
)
{
HRESULT hr = S_OK;
LPWSTR pszServerTemp = NULL;
ULONG ulMutualAuth = 0;
VariantInit(pvProp);
switch (lnControlCode) {
case ADS_OPTION_SERVERNAME:
hr = GetOption(LDAP_SERVER, (void *) &pszServerTemp);
BAIL_ON_FAILURE(hr);
pvProp->vt = VT_BSTR;
hr = ADsAllocString(
pszServerTemp,
&(pvProp->bstrVal)
);
break;
case ADS_OPTION_SECURITY_MASK:
//
// No need to call GetOpion at least not now
//
pvProp->vt = VT_I4;
pvProp->lVal = (ULONG) _seInfo;
break;
case ADS_OPTION_REFERRALS :
pvProp->vt = VT_I4;
switch (_dwOptReferral) {
case ((DWORD) (DWORD_PTR)LDAP_OPT_OFF) :
pvProp->lVal = ADS_CHASE_REFERRALS_NEVER;
break;
case LDAP_CHASE_SUBORDINATE_REFERRALS :
pvProp->lVal = ADS_CHASE_REFERRALS_SUBORDINATE;
break;
case LDAP_CHASE_EXTERNAL_REFERRALS :
pvProp->lVal = ADS_CHASE_REFERRALS_EXTERNAL;
break;
case ((DWORD) (DWORD_PTR)LDAP_OPT_ON) :
pvProp->lVal = ADS_CHASE_REFERRALS_ALWAYS;
break;
default:
pvProp->lVal = 0;
hr = E_ADS_PROPERTY_INVALID;
}
break;
case ADS_OPTION_PAGE_SIZE :
pvProp->vt = VT_I4;
pvProp->lVal = (ULONG) _dwPageSize;
break;
case ADS_OPTION_MUTUAL_AUTH_STATUS :
hr = GetOption(LDAP_MUTUAL_AUTH_STATUS, (void *) &ulMutualAuth);
BAIL_ON_FAILURE(hr);
pvProp->vt = VT_I4;
pvProp->lVal = ulMutualAuth;
break;
default:
hr = E_ADS_BAD_PARAMETER;
}
error :
if (pszServerTemp) {
FreeADsStr(pszServerTemp);
}
RRETURN(hr);
}
STDMETHODIMP
CLDAPGenObject::SetOption(
THIS_ long lnControlCode,
VARIANT vProp
)
{
HRESULT hr = S_OK;
DWORD dwOptVal = 0;
VARIANT *pvProp = NULL;
//
// To make sure we handle variant by refs correctly.
//
pvProp = &vProp;
if (V_VT(pvProp) == (VT_BYREF|VT_VARIANT)) {
pvProp = V_VARIANTREF(&vProp);
}
switch (lnControlCode) {
case ADS_OPTION_REFERRALS :
hr = GetIntegerFromVariant(pvProp, &dwOptVal);
if (SUCCEEDED(hr))
hr = SetOption(LDAP_CHASE_REFERRALS, (void *) &dwOptVal);
break;
case ADS_OPTION_PAGE_SIZE :
hr = GetIntegerFromVariant(pvProp, &dwOptVal);
if (SUCCEEDED(hr))
hr = SetOption(LDAP_PAGESIZE, (void *) &dwOptVal);
break;
case ADS_OPTION_SECURITY_MASK :
hr = GetIntegerFromVariant(pvProp, &dwOptVal);
if (SUCCEEDED(hr)) {
_seInfo = (SECURITY_INFORMATION) dwOptVal;
_fExplicitSecurityMask = TRUE;
}
break;
case ADS_PRIVATE_OPTION_SPECIFIC_SERVER :
//
// If it is just a VT_BSTR, then this is old.
// If this is an array, then it should also
// have the domain information.
//
if (pvProp->vt == VT_BSTR) {
if (gpszStickyDomainName) {
FreeADsStr(gpszStickyDomainName);
gpszStickyDomainName = NULL;
}
if (gpszStickyServerName) {
FreeADsStr(gpszStickyServerName);
gpszStickyServerName = NULL;
}
//
// Set for LDAP layer
//
if (pvProp->bstrVal) {
gpszStickyServerName = AllocADsStr(pvProp->bstrVal);
if (!gpszStickyServerName) {
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr)) {
hr = LdapcSetStickyServer(NULL, pvProp->bstrVal);
}
}
else if ((pvProp->vt & VT_ARRAY)) {
hr = SetStickyServerWithDomain(pvProp);
}
break;
default:
hr = E_ADS_BAD_PARAMETER;
}
RRETURN(hr);
}
HRESULT
ConvertVariantToVariantArray(
VARIANT varData,
VARIANT ** ppVarArray,
DWORD * pdwNumValues
)
{
DWORD dwNumValues = 0;
VARIANT * pVarArray = NULL;
HRESULT hr = S_OK;
VARIANT * pvarData = NULL;
*ppVarArray = NULL;
*pdwNumValues = 0;
//
// Although no known automation controller passes
// VT_VARIANT|VT_BYREF into get_/put_ function
// as a reference to an array, we carry out the following
// check for extra safety.
//
pvarData = &varData;
if (V_VT(pvarData) == (VT_VARIANT|VT_BYREF)) {
pvarData = V_VARIANTREF(&varData);
}
if ((V_VT(pvarData) == (VT_VARIANT|VT_ARRAY|VT_BYREF)) ||
(V_VT(pvarData) == (VT_VARIANT|VT_ARRAY))) {
hr = ConvertSafeArrayToVariantArray(
*pvarData,
&pVarArray,
&dwNumValues
);
// returns E_FAIL if *pvarData is invalid
if (hr == E_FAIL)
hr = E_ADS_BAD_PARAMETER;
BAIL_ON_FAILURE(hr);
} else {
pVarArray = NULL;
dwNumValues = 0;
}
*ppVarArray = pVarArray;
*pdwNumValues = dwNumValues;
error:
RRETURN(hr);
}
void
FreeVariantArray(
VARIANT * pVarArray,
DWORD dwNumValues
)
{
if (pVarArray) {
DWORD i = 0;
for (i = 0; i < dwNumValues; i++) {
VariantClear(pVarArray + i);
}
FreeADsMem(pVarArray);
}
}
HRESULT
ConvertVariantToLdapValues(
VARIANT varData,
LPWSTR szPropertyName,
PDWORD pdwControlCode,
LDAPOBJECTARRAY * pldapDestObjects,
PDWORD pdwSyntaxId,
LPWSTR pszServer,
CCredentials* pCredentials,
DWORD dwPort
)
{
HRESULT hr = S_OK;
IADsPropertyEntry * pPropEntry = NULL;
IDispatch * pDispatch = NULL;
BSTR bstrPropName = NULL;
DWORD dwControlCode = 0;
DWORD dwAdsType = 0;
VARIANT varValues;
VARIANT * pVarArray = NULL;
DWORD dwNumValues = 0;
PADSVALUE pAdsValues = NULL;
DWORD dwAdsValues = 0;
PVARIANT pVar = NULL;
BOOL fNTDSType = TRUE;
BOOL fGenTime = FALSE;
DWORD dwServerSyntaxId = 0;
if (V_VT(&varData) != VT_DISPATCH) {
if (V_VT(&varData) != (VT_VARIANT | VT_BYREF)) {
RRETURN (hr = DISP_E_TYPEMISMATCH);
}
else {
pVar = V_VARIANTREF(&varData);
if (pVar == NULL || V_VT(pVar) != VT_DISPATCH) {
RRETURN (hr = DISP_E_TYPEMISMATCH);
}
else {
pDispatch = V_DISPATCH(pVar);
}
}
}
else {
pDispatch = V_DISPATCH(&varData);
}
VariantInit(&varValues);
hr = pDispatch->QueryInterface(
IID_IADsPropertyEntry,
(void **)&pPropEntry
);
BAIL_ON_FAILURE(hr);
hr = pPropEntry->get_Name(&bstrPropName);
BAIL_ON_FAILURE(hr);
wcscpy(szPropertyName, bstrPropName);
hr = pPropEntry->get_ControlCode((long *)&dwControlCode);
BAIL_ON_FAILURE(hr);
hr = pPropEntry->get_ADsType((long *)&dwAdsType);
BAIL_ON_FAILURE(hr);
hr = pPropEntry->get_Values(&varValues);
BAIL_ON_FAILURE(hr);
hr = ConvertVariantToVariantArray(
varValues,
&pVarArray,
&dwNumValues
);
BAIL_ON_FAILURE(hr);
if (dwNumValues) {
//
// At this point it is probably cheaper to read the
// server type and send the values to the conversion
// routines than to check if this is a securityDescriptor
// by walking the PropertyEntry and then looking at the variant.
// There is a good chance that this information is already cached.
//
hr = ReadServerType(
pszServer,
pCredentials,
&fNTDSType
);
BAIL_ON_FAILURE(hr);
hr = PropVariantToAdsType2(
pVarArray,
dwNumValues,
&pAdsValues,
&dwAdsValues,
pszServer,
pCredentials,
fNTDSType
);
BAIL_ON_FAILURE(hr);
if (dwAdsType == ADSTYPE_UTC_TIME) {
//
// See if this is a GenTime on the server
//
HRESULT hr;
hr = LdapGetSyntaxOfAttributeOnServer(
pszServer,
szPropertyName,
&dwServerSyntaxId,
*pCredentials,
dwPort
);
if (FAILED(hr)) {
hr = S_OK;
}
else if (dwServerSyntaxId == LDAPTYPE_GENERALIZEDTIME) {
fGenTime = TRUE;
}
}
hr = AdsTypeToLdapTypeCopyConstruct(
pAdsValues,
dwAdsValues,
pldapDestObjects,
pdwSyntaxId,
fGenTime
);
BAIL_ON_FAILURE(hr);
}
*pdwControlCode = dwControlCode;
cleanup:
if (bstrPropName) {
ADsFreeString(bstrPropName);
}
if (pAdsValues) {
AdsTypeFreeAdsObjects(
pAdsValues,
dwNumValues
);
}
if (pVarArray) {
FreeVariantArray(
pVarArray,
dwAdsValues
);
}
if (pPropEntry) {
pPropEntry->Release();
}
VariantClear(&varValues);
RRETURN(hr);
error:
LdapTypeFreeLdapObjects( pldapDestObjects );
goto cleanup;
}
HRESULT
MapAdsTypeToLdapType(
BSTR bstrPropName,
DWORD dwAdsType,
PDWORD pdwLdapType
)
{
RRETURN(S_OK);
}
HRESULT
ConvertLdapValuesToVariant(
BSTR bstrPropName,
LDAPOBJECTARRAY * pldapSrcObjects,
DWORD dwLdapType,
DWORD dwControlCode,
PVARIANT pVarProp,
LPWSTR pszServer,
CCredentials* pCredentials
)
{
HRESULT hr = S_OK;
PADSVALUE pAdsValues = NULL;
DWORD dwNumAdsValues = 0;
DWORD dwNumValues = 0;
DWORD dwAdsType = 0;
VARIANT varData;
IDispatch * pDispatch = NULL;
BOOL fNTDS = TRUE;
VariantInit(&varData);
VariantClear(&varData);
VariantInit(pVarProp);
// pldaSrcObject should never be null
ADsAssert(pldapSrcObjects);
if (dwControlCode != ADS_PROPERTY_DELETE ) {
hr = LdapTypeToAdsTypeCopyConstruct(
*pldapSrcObjects,
dwLdapType,
&pAdsValues,
&dwNumAdsValues,
&dwAdsType
);
if (SUCCEEDED(hr)) {
dwNumValues = pldapSrcObjects->dwCount;
//
// if the property is a security descriptor
// we need to set the server type also
//
if (dwAdsType == ADSTYPE_NT_SECURITY_DESCRIPTOR) {
hr = ReadServerType(
pszServer,
pCredentials,
&fNTDS
);
BAIL_ON_FAILURE(hr);
hr = AdsTypeToPropVariant2(
pAdsValues,
dwNumValues,
&varData,
pszServer,
pCredentials,
fNTDS
);
} else {
hr = AdsTypeToPropVariant(
pAdsValues,
dwNumValues,
&varData
);
}
BAIL_ON_FAILURE(hr);
} else {
// We could not convert the data type
// This maybe because we have the invalid data type
// so we will just return the variant data as null
// and also set the data type to 0 or invalid in that case
if (dwLdapType == LDAPTYPE_UNKNOWN) {
dwAdsType = 0;
hr = S_OK;
} else {
// since the datatpye was valid, we should
// send this back to the user.
BAIL_ON_FAILURE(hr);
}
}
} else {
dwAdsType = 0;
}
hr = CreatePropEntry(
bstrPropName,
dwAdsType,
dwNumValues,
dwControlCode,
varData,
IID_IDispatch,
(void **)&pDispatch
);
BAIL_ON_FAILURE(hr);
V_DISPATCH(pVarProp) = pDispatch;
V_VT(pVarProp) = VT_DISPATCH;
error:
VariantClear(&varData);
if (pAdsValues) {
AdsTypeFreeAdsObjects(
pAdsValues,
dwNumValues
);
}
RRETURN(hr);
}
//
// Needed for dynamic dispid's in the property cache.
//
HRESULT
CLDAPGenObject::GetAttributeSyntax(
LPWSTR szPropertyName,
PDWORD pdwSyntaxId
)
{
HRESULT hr;
hr = LdapGetSyntaxOfAttributeOnServer(
_pszLDAPServer,
szPropertyName,
pdwSyntaxId,
_Credentials,
_dwPort
);
RRETURN_EXP_IF_ERR(hr);
}
HRESULT
CLDAPGenObject::DeleteObject(
long lnFlags
)
{
HRESULT hr = S_OK;
LDAPControl SeInfoControlRecursDelete =
{
LDAP_SERVER_TREE_DELETE_OID_W,
{ NULL, NULL},
FALSE
};
PLDAPControl ServerControls[2] =
{
&SeInfoControlRecursDelete,
NULL
};
hr = LdapDeleteExtS(
_pLdapHandle,
_pszLDAPDn,
(PLDAPControl *)&ServerControls,
NULL
);
BAIL_ON_FAILURE(hr);
error:
RRETURN(hr);
}
HRESULT
CLDAPGenObject::GetActualHostName(
LPWSTR * pValue
)
{
HRESULT hr = S_OK;
DWORD dwLength = MAX_PATH;
LPWSTR szHostName = NULL;
int err = 0;
LDAPMessage *pMsgResult = NULL;
LDAPMessage *pMsgEntry = NULL;
LDAP *pLdapCurrent = NULL;
LPWSTR Attributes[] = {L"objectClass", NULL};
//
// We need to get at the actual object as we may have a
// referral
//
hr = LdapSearchS(
_pLdapHandle,
_pszLDAPDn,
LDAP_SCOPE_BASE,
L"(objectClass=*)",
Attributes,
0,
&pMsgResult
);
//
// Only one entry should be returned
//
BAIL_ON_FAILURE(hr);
hr = LdapFirstEntry(
_pLdapHandle,
pMsgResult,
&pMsgEntry
);
BAIL_ON_FAILURE(hr);
pLdapCurrent = pMsgResult->Connection;
err = ldap_get_optionW(
pLdapCurrent,
LDAP_OPT_HOST_NAME,
&szHostName
);
if (err != LDAP_SUCCESS || szHostName == NULL) {
BAIL_ON_FAILURE(hr = E_FAIL);
}
// If we are here we need to copy the name and return
*pValue = AllocADsStr(szHostName);
if(!(*pValue)) {
hr = E_OUTOFMEMORY;
BAIL_ON_FAILURE(hr);
}
error:
if (pMsgResult) {
LdapMsgFree(pMsgResult);
}
RRETURN(hr);
}
HRESULT
GetIntegerFromVariant(
VARIANT* pvProp,
DWORD* pdwVal)
{
HRESULT hr = S_OK;
*pdwVal = 0;
if (pvProp->vt == VT_I4) {
*pdwVal = pvProp->lVal;
}
else if(pvProp->vt == VT_I2) {
*pdwVal = pvProp->iVal;
} else
hr = E_ADS_BAD_PARAMETER;
RRETURN(hr);
}
//+------------------------------------------------------------------------
//
// Function: TraceTreeForClass
//
// Synopsis: Traces the inheritance hierarchy for the class being crerated
// and returns the list in the arg pppszNameArr. There are
// *plnNumElements in this array. This can be used later while
// creating the object so that all the extensions will be available.
//
// Arguments:
// Parent - The ADsPath of the parent,
// CommonName - RDN of the object being created,
// pszClassName- Class of the object being created,
// Credentials - Credentials blob,
// pppszNameArr- Return value - array of names of parent classes.
// plnNumItems - Return value - number of elements in above array.
//
//-------------------------------------------------------------------------
HRESULT
TraceTreeForClass(
BSTR Parent,
BSTR CommonName,
LPWSTR pszClassName,
CCredentials& Credentials,
PWCHAR **pppszNameArr,
PLONG plnNumElements
)
{
HRESULT hr = S_OK;
IADsClass *pIADsClass = NULL;
IUnknown *pUnk = NULL;
PWCHAR *pszRetVal = NULL;
WCHAR pszSchemaPathBase[MAX_PATH];
WCHAR pszSchemaPath[MAX_PATH];
LPWSTR pszServer = NULL;
LPWSTR pszLDAPDn = NULL;
LPWSTR pszCurVal = NULL;
DWORD dwPort = 0;
VARIANT vBstrVal;
CCredentials Creds = Credentials;
long lnNumItems = 1;
BOOL fDone = FALSE;
PClassesHierarchyList pClassListHead = NULL;
PClassesHierarchyList pClassListNode = NULL;
//
// We need to make sure that the ADS_AUTH_RESERVED flag is not set
// in the credentials because that will result in us not getting the
// IADsClass interface ptr we want.
//
Creds.SetAuthFlags(Creds.GetAuthFlags() & ~ADS_AUTH_RESERVED);
VariantInit(&vBstrVal);
//
// Build The schema name
//
hr = BuildLDAPPathFromADsPath2(
Parent,
&pszServer,
&pszLDAPDn,
&dwPort
);
BAIL_ON_FAILURE(hr);
if (pszServer) {
wsprintf(pszSchemaPathBase, L"LDAP://%s", pszServer);
} else {
wsprintf(pszSchemaPathBase, L"LDAP:/");
}
pszCurVal = AllocADsStr(pszClassName);
if (!pszCurVal) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
while (!fDone && _wcsicmp(L"Top", pszCurVal)) {
lnNumItems++;
//
// Add a new node to the list
//
pClassListNode = (PClassesHierarchyList)
AllocADsMem(sizeof(ClassesHierarchyList));
if (!pClassListNode) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pClassListNode->pszClassName = AllocADsStr(pszCurVal);
if (!pClassListNode->pszClassName) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pClassListNode->pNext = pClassListHead;
pClassListHead = pClassListNode;
//
// Check for the next class on the list
//
wsprintf(pszSchemaPath, L"%s", pszSchemaPathBase);
wcscat(pszSchemaPath, L"/Schema/");
wcscat(pszSchemaPath, pszCurVal);
FreeADsStr(pszCurVal);
pszCurVal = NULL;
hr = GetObject(
pszSchemaPath,
Credentials,
(LPVOID *) &pUnk
);
BAIL_ON_FAILURE(hr);
hr = pUnk->QueryInterface(
IID_IADsClass,
(void **) &pIADsClass
);
BAIL_ON_FAILURE(hr);
//
// Release the ref on the pUnk
//
pUnk->Release();
pUnk = NULL;
hr = pIADsClass->get_DerivedFrom(&vBstrVal);
BAIL_ON_FAILURE(hr);
//
// Release the ptr as we no longer need it.
//
pIADsClass->Release();
if (vBstrVal.vt == (VT_VARIANT | VT_ARRAY)) {
//
// Server has complete list of classes in schema
//
hr = AddToClassesList(
vBstrVal,
&pszCurVal,
&pClassListHead,
&lnNumItems
);
BAIL_ON_FAILURE(hr);
//
// add one to the count for the value passed in
//
lnNumItems++;
fDone = TRUE;
}
else if (vBstrVal.vt == VT_BSTR) {
pszCurVal = AllocADsStr(vBstrVal.bstrVal);
VariantClear(&vBstrVal);
if (!pszCurVal) {
hr = E_OUTOFMEMORY;
}
}
else {
hr = E_FAIL;
}
BAIL_ON_FAILURE(hr);
}
//
// We now have the list as well as the number of items
//
pszRetVal = (PWCHAR *) AllocADsMem(sizeof(PWCHAR) * (lnNumItems + 1));
if (!pszRetVal) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
//
// Do not move as we may call -- below
//
*plnNumElements = lnNumItems;
pszRetVal[lnNumItems] = NULL;
if (!fDone) {
//
// Add top to the list and set last to NULL
// only if we did not hit addclasses fn.
//
pszRetVal[lnNumItems - 1] = AllocADsStr(L"Top");
if (!pszRetVal[lnNumItems - 1]) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
lnNumItems--;
}
while (pClassListHead && (lnNumItems > -1)) {
pszRetVal[--lnNumItems] =
AllocADsStr(pClassListHead->pszClassName);
if (!pszRetVal[lnNumItems]) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
//
// Free the entry and advance list
//
FreeADsStr(pClassListHead->pszClassName);
pClassListNode = pClassListHead;
pClassListHead = pClassListHead->pNext;
FreeADsMem(pClassListNode);
pClassListNode = NULL;
}
//
// Put appropriate value in return arg.
//
*pppszNameArr = pszRetVal;
error:
if (pszServer) {
FreeADsStr(pszServer);
}
if (pszLDAPDn) {
FreeADsStr(pszLDAPDn);
}
if (pszCurVal) {
FreeADsStr(pszCurVal);
}
if (pUnk) {
pUnk->Release();
}
//
// Walk through and free the list if necessary
//
while (pClassListHead) {
pClassListNode = pClassListHead;
if (pClassListHead->pszClassName) {
FreeADsStr(pClassListHead->pszClassName);
}
pClassListHead = pClassListHead->pNext;
FreeADsMem(pClassListNode);
}
RRETURN(hr);
}
//
// Helper to get the values from the Variant and add to list
//
HRESULT
AddToClassesList(
VARIANT vProps,
LPWSTR *ppszCurClass,
PClassesHierarchyList *ppClassListHead,
PLONG plnNumItems
)
{
HRESULT hr = S_OK;
PClassesHierarchyList pClassNode = NULL;
VARIANT * pVarArray = NULL;
VARIANT * pvProp = NULL;
DWORD dwNumValues = 0, i = 0;
pvProp = &vProps;
hr = ConvertSafeArrayToVariantArray(
*pvProp,
&pVarArray,
&dwNumValues
);
BAIL_ON_FAILURE(hr);
//
// Go through the array adding nodes.
//
for (i = 0; i < dwNumValues; i++) {
pvProp = pVarArray + i;
if (pvProp->vt != VT_BSTR) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
//
// Alloc node to add
//
pClassNode = (PClassesHierarchyList)
AllocADsMem(sizeof(ClassesHierarchyList));
if (!pClassNode) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pClassNode->pszClassName = AllocADsStr(pvProp->bstrVal);
if (!pClassNode->pszClassName) {
//
// Free pClassNode as we will let caller free the list
// if we run out of memory.
//
FreeADsMem(pClassNode);
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
pClassNode->pNext = *ppClassListHead;
*ppClassListHead = pClassNode;
} // end for
*plnNumItems = dwNumValues;
error:
if (pVarArray) {
for (i = 0; i < dwNumValues; i++) {
VariantClear(pVarArray + i);
}
FreeADsMem(pVarArray);
}
RRETURN(hr);
}
//
// Helper routine that handles setting the sticky server private
// option when the input is an array.
//
HRESULT
SetStickyServerWithDomain(
PVARIANT pvProp
)
{
HRESULT hr = S_OK;
VARIANT *pvVarArray = NULL;
DWORD dwNumVariants = 0;
DWORD dwCtr = 0;
LPWSTR *ppszStringArray = NULL;
hr = ConvertSafeArrayToVariantArray(
*pvProp,
&pvVarArray,
&dwNumVariants
);
// returns E_FAIL if vProperties is invalid
if (hr == E_FAIL)
hr = E_ADS_BAD_PARAMETER;
BAIL_ON_FAILURE(hr);
//
// There have to be precisely 2 entries, one for domain
// and the second for the serverName.
//
if (dwNumVariants != 2) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
hr = ConvertVariantArrayToLDAPStringArray(
pvVarArray,
&ppszStringArray,
dwNumVariants
);
BAIL_ON_FAILURE(hr);
if (!ppszStringArray
|| !ppszStringArray[0]
|| !*(ppszStringArray[0])
|| !ppszStringArray[1]
|| !*(ppszStringArray[1])
) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
if (gpszStickyServerName) {
FreeADsStr(gpszStickyServerName);
gpszStickyServerName = NULL;
}
if (gpszStickyDomainName) {
FreeADsStr(gpszStickyDomainName);
gpszStickyDomainName = NULL;
}
gpszStickyServerName = AllocADsStr(ppszStringArray[1]);
if (!gpszStickyServerName) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
gpszStickyDomainName = AllocADsStr(ppszStringArray[0]);
if (!gpszStickyDomainName) {
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
}
hr = LdapcSetStickyServer(
gpszStickyDomainName,
gpszStickyServerName
);
BAIL_ON_FAILURE(hr);
error:
if (FAILED(hr)) {
//
// Clear the global strings on failure.
//
if (gpszStickyServerName) {
FreeADsStr(gpszStickyServerName);
gpszStickyServerName = NULL;
}
if (gpszStickyDomainName) {
FreeADsStr(gpszStickyDomainName);
gpszStickyDomainName = NULL;
}
}
//
// Cleanup variant array and string array.
//
if (pvVarArray) {
for (dwCtr = 0; dwCtr < dwNumVariants; dwCtr++) {
VariantClear(pvVarArray + dwCtr);
}
FreeADsMem(pvVarArray);
}
if (ppszStringArray) {
for (dwCtr = 0; dwCtr < dwNumVariants; dwCtr++) {
if (ppszStringArray[dwCtr])
FreeADsStr(ppszStringArray[dwCtr]);
}
FreeADsMem(ppszStringArray);
}
RRETURN(hr);
}