//---------------------------------------------------------------------------- // // 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" wcslen(L"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 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 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 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 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); }