// // 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 HRESULT CLDAPGenObject::get_PropertyCount( THIS_ long FAR *plCount ) { HRESULT hr = E_FAIL; BSTR bstrProperty = NULL; SAFEARRAY *psaProperty = NULL; SAFEARRAYBOUND rgsabound[1]; long lsaDim[1]; VARIANT varProperty; // ??? _pPropertyCache canNOT be NULL or bailed out during // CLDAPGenObject creation already // assert(_PropertyCache); if (_pPropertyCache) { hr = _pPropertyCache->get_PropertyCount((PDWORD)plCount); } RRETURN_EXP_IF_ERR(hr); } //////////////////////////////////////////////////////////////////////////// // // - Return the "next" item (item with the current index) in cache, if any, // in a property entry [*pVariant]. // - Return E_ADS_PROPERTY_NOT_FOUND when current index is out of bound. // //////////////////////////////////////////////////////////////////////////// STDMETHODIMP CLDAPGenObject::Next( THIS_ VARIANT FAR *pVariant ) { HRESULT hr = E_FAIL; DWORD dwSyntaxId = 0; DWORD dwNumValues = 0; LDAPOBJECTARRAY ldapSrcObjects; IDispatch * pDispatch = NULL; DWORD dwNumAdsValues = 0; DWORD dwAdsType = 0; DWORD dwPropStatus = 0; DWORD dwCtrlCode = 0; if(!_pPropertyCache->index_valid()) RRETURN_EXP_IF_ERR(E_FAIL); LDAPOBJECTARRAY_INIT(ldapSrcObjects); hr = _pPropertyCache->unboundgetproperty( _pPropertyCache->get_CurrentIndex(), &dwSyntaxId, &dwPropStatus, &ldapSrcObjects ); BAIL_ON_FAILURE(hr); dwCtrlCode = MapPropCacheFlagToControlCode(dwPropStatus); // // translate the LDAP objects to variants // hr = ConvertLdapValuesToVariant( _pPropertyCache->get_CurrentPropName(), &ldapSrcObjects, dwSyntaxId, dwCtrlCode, pVariant, _pszLDAPServer, &_Credentials ); BAIL_ON_FAILURE(hr); error: // // - goto next one even if error to avoid infinite looping at a property // which we cannot convert (e.g. schemaless server property.) // - do not return the result of Skip() as current operation does not // depend on the sucess of Skip(). // Skip(1); LdapTypeFreeLdapObjects(&ldapSrcObjects); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPGenObject::Skip( THIS_ long cElements ) { HRESULT hr = S_OK; hr = _pPropertyCache->skip_propindex( cElements ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPGenObject::Reset( ) { _pPropertyCache->reset_propindex(); RRETURN_EXP_IF_ERR(S_OK); } STDMETHODIMP CLDAPGenObject::ResetPropertyItem(THIS_ VARIANT varEntry) { HRESULT hr = S_OK; DWORD dwIndex = 0; switch (V_VT(&varEntry)) { case VT_BSTR: hr = _pPropertyCache->findproperty( V_BSTR(&varEntry), &dwIndex ); BAIL_ON_FAILURE(hr); break; case VT_I4: dwIndex = V_I4(&varEntry); break; case VT_I2: dwIndex = V_I2(&varEntry); break; default: hr = E_FAIL; BAIL_ON_FAILURE(hr); } hr = _pPropertyCache->deleteproperty( dwIndex ); error: RRETURN_EXP_IF_ERR(hr); } ////////////////////////////////////////////////////////////////////////// // // Retrieve property [bstrName] from the cache (only, no wire calls) as // a PropertyEntry. // // [*pVariant] // - store ptr to IDispatch of the PropertyEntry. // // If the property in cache has control code = ADS_PROPERTY_DELETE, // - PropertyEntry will contain an empty variant and // - adstype = ADSTYPE_INVALID. // // If property in cache has UNKNWON type, (not deleted, for schemaless-server // property which is not in ADSI default schema) // - [lnAdsType] must be a valid type (NO ADSTYPE_UNKNWON/INVALID) // - property will be retrieved as [lnADsType] // // If property in cache has KNOWN type, // - [lnADsType] must either match type in cache or == ADSTYPE_UNKNOWN // - property will be retreived as the type in cache. // ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CLDAPGenObject::GetPropertyItem( THIS_ IN BSTR bstrName, IN LONG lnADsType, IN OUT VARIANT * pVariant ) { HRESULT hr = S_OK; DWORD dwCachedSyntax = LDAPTYPE_UNKNOWN; DWORD dwUserSyntax = LDAPTYPE_UNKNOWN; DWORD dwSyntaxUsed = LDAPTYPE_UNKNOWN; // extra, make code easier to read DWORD dwPropStatus = 0; DWORD dwCtrlCode = 0; LDAPOBJECTARRAY ldapSrcObjects; LDAPOBJECTARRAY ldapSrc2Objects; LDAPOBJECTARRAY * pLdapSrcObjects = NULL; LDAPOBJECTARRAY_INIT(ldapSrcObjects); LDAPOBJECTARRAY_INIT(ldapSrc2Objects); if (!bstrName || !pVariant) RRETURN(E_ADS_BAD_PARAMETER); // // retrieve property from cache; CONTINUE if property exist but // has no value (control code flag as a DELETE) // hr = _pPropertyCache->unboundgetproperty( bstrName, &dwCachedSyntax, &dwPropStatus, &ldapSrcObjects ); BAIL_ON_FAILURE(hr); // For backward compatibility -- no issue as you // need to return a value even if it is delete. // // map adstype from client to ldap type; // dwUserSyntax = MapADSTypeToLDAPType((ADSTYPE)lnADsType); // // determine the syntax to retrieve property in // if ( (dwCachedSyntax == LDAPTYPE_UNKNOWN) || (dwCachedSyntax == 0) // should NOT be 0, but misuse of 0 everywhere ) // and in case i didn't clean up all { // // syntax not stored in cache, user must spcify a valid sytax // Exception: cleared property values have LDAPTYPE_UNKNOWN, and we // return them as ADSTYPE_UNKNOWN // if ((dwUserSyntax == LDAPTYPE_UNKNOWN) && (dwPropStatus != PROPERTY_DELETE)) { hr = E_ADS_CANT_CONVERT_DATATYPE; BAIL_ON_FAILURE(hr); } dwSyntaxUsed = dwUserSyntax; // // convert from cached data from ldap binary format to ldap string // IFF necessary based on dwUserSyntax // hr = LdapTypeBinaryToString( dwSyntaxUsed, &ldapSrcObjects, &ldapSrc2Objects ); // // dwSyntaxUsed (dwUserSyntax) must be valid from // MapADSTypeToLDAPType() or code bug in MapADsTypeToLDAPType() !! // ADsAssert(SUCCEEDED(hr)); if (hr==S_OK) { pLdapSrcObjects = &ldapSrc2Objects; // conversion done } else // hr == S_FALSE { pLdapSrcObjects = &ldapSrcObjects; // no conversion } } else // dwCachedSyntax known and valid { // // syntax stored in cache, user MUST either specify // 1) ADSTYPE_UNKNWON or // 2) a syntax which matches the one in cache. The comparision must // be done in ADsType, not LdapType, since LdapType To ADSType // is n to 1 mapping and as long as ADsType match, ok. // if ( ! ( (dwUserSyntax == LDAPTYPE_UNKNOWN) || ( (ADSTYPE) lnADsType == MapLDAPTypeToADSType(dwCachedSyntax)) ) ) { if (dwUserSyntax != dwCachedSyntax) { // // Check if the user wants the data back for the // security descriptor as an octet or vice versa // if ( (dwUserSyntax == LDAPTYPE_OCTETSTRING && dwCachedSyntax == LDAPTYPE_SECURITY_DESCRIPTOR) ||(dwUserSyntax == LDAPTYPE_SECURITY_DESCRIPTOR && dwCachedSyntax == LDAPTYPE_OCTETSTRING)) { dwCachedSyntax = dwUserSyntax; } else { // // Check for UTC/GenTime mismatch before ret error. // if (!((dwCachedSyntax == LDAPTYPE_GENERALIZEDTIME) && (dwUserSyntax == LDAPTYPE_UTCTIME))) { hr = E_ADS_CANT_CONVERT_DATATYPE; BAIL_ON_FAILURE(hr); } } } // if dwUserSyntax != dwCachedSyntax } dwSyntaxUsed = dwCachedSyntax; pLdapSrcObjects = &ldapSrcObjects; // no conversion needed } // // translate ldap prop status to ads control code // dwCtrlCode = MapPropCacheFlagToControlCode(dwPropStatus); // // translate the property from Ldap objects to a PropertyEntry // hr = ConvertLdapValuesToVariant( bstrName, pLdapSrcObjects, dwSyntaxUsed, dwCtrlCode, pVariant, _pszLDAPServer, &_Credentials ); error: LdapTypeFreeLdapObjects( &ldapSrcObjects ); LdapTypeFreeLdapObjects( &ldapSrc2Objects ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPGenObject::PutPropertyItem( THIS_ VARIANT varData ) { HRESULT hr = S_OK; DWORD dwFlags = 0; DWORD dwIndex = 0; DWORD dwControlCode = 0; LDAPOBJECTARRAY ldapDestObjects; WCHAR szPropertyName[MAX_PATH]; DWORD dwSyntaxId = 0; LDAPOBJECTARRAY_INIT(ldapDestObjects); hr = ConvertVariantToLdapValues( varData, szPropertyName, &dwControlCode, &ldapDestObjects, &dwSyntaxId, _pszLDAPServer, &_Credentials, _dwPort ); BAIL_ON_FAILURE(hr); switch ( dwControlCode ) { case 0 : // // Users better know what they are doing here, // This the property as cleared so we do not send it // on the wire on th next SetInfo. // dwFlags = PROPERTY_INIT; break; case ADS_PROPERTY_CLEAR: // // Clears an entire property // dwFlags = PROPERTY_DELETE; break; case ADS_PROPERTY_UPDATE: // // Updates the entire property // dwFlags = PROPERTY_UPDATE; break; case ADS_PROPERTY_APPEND: // // Appends a set of values to the property // dwFlags = PROPERTY_ADD; break; case ADS_PROPERTY_DELETE: // // Delete a value(s) from the property dwFlags = PROPERTY_DELETE_VALUE; break; default: BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER); } // // Find this property in the cache // hr = _pPropertyCache->findproperty( szPropertyName, &dwIndex ); // // If this property does not exist in the // cache, add this property into the cache. // if (FAILED(hr)) { hr = _pPropertyCache->addproperty( szPropertyName ); // // If dwNumValues == 0 ( delete the property ) but couldn't find // the property, or if the add operation fails, return the error. // BAIL_ON_FAILURE(hr); } // // Now update the property in the cache // hr = _pPropertyCache->putproperty( szPropertyName, dwFlags, dwSyntaxId, ldapDestObjects ); BAIL_ON_FAILURE(hr); error: LdapTypeFreeLdapObjects( &ldapDestObjects ); RRETURN_EXP_IF_ERR(hr); } HRESULT CreatePropEntry( LPWSTR szPropName, DWORD ADsType, DWORD numValues, DWORD dwOperation, VARIANT varData, REFIID riid, LPVOID * ppDispatch ) { HRESULT hr = S_OK; IADsPropertyEntry * pPropEntry = NULL; hr = CoCreateInstance( CLSID_PropertyEntry, NULL, CLSCTX_INPROC_SERVER, IID_IADsPropertyEntry, (void **)&pPropEntry ); BAIL_ON_FAILURE(hr); hr = pPropEntry->put_Name(szPropName); BAIL_ON_FAILURE(hr); hr = pPropEntry->put_ADsType(ADsType); BAIL_ON_FAILURE(hr); hr = pPropEntry->put_Values(varData); BAIL_ON_FAILURE(hr); if (dwOperation) { hr = pPropEntry->put_ControlCode((long)dwOperation); } BAIL_ON_FAILURE(hr); hr = pPropEntry->QueryInterface( riid, ppDispatch ); BAIL_ON_FAILURE(hr); error: if (pPropEntry) { pPropEntry->Release(); } RRETURN(hr); } STDMETHODIMP CLDAPGenObject::Item( THIS_ VARIANT varIndex, VARIANT * pVariant ) { HRESULT hr = S_OK; DWORD dwSyntaxId; LDAPOBJECTARRAY ldapSrcObjects; PADSVALUE pAdsValues = NULL; DWORD dwNumAdsValues = 0; DWORD dwAdsType = 0; DWORD dwNumValues = 0; LPWSTR szPropName = NULL; DWORD dwPropStatus = 0; DWORD dwCtrlCode = (DWORD) -1; VARIANT * pvVar = &varIndex; LDAPOBJECTARRAY_INIT(ldapSrcObjects); // // retrieve data object from cache; if one exis // // If the object has been deleted (and is in the cache // marked for deletion), we return // the item with DELETE ctrl code in all // the cases below. This is consistent with GetPropertyItem's // behavior as well. if (V_VT(pvVar) == (VT_BYREF|VT_VARIANT)) { // // The value is being passed in byref so we need to // deref it for vbs stuff to work // pvVar = V_VARIANTREF(&varIndex); } switch (V_VT(pvVar)) { case VT_BSTR: // // retrieve data object from cache; if one exists // hr = _pPropertyCache->unboundgetproperty( V_BSTR(pvVar), &dwSyntaxId, &dwPropStatus, &ldapSrcObjects ); BAIL_ON_FAILURE(hr); dwCtrlCode = MapPropCacheFlagToControlCode(dwPropStatus); hr = ConvertLdapValuesToVariant( V_BSTR(pvVar), &ldapSrcObjects, dwSyntaxId, dwCtrlCode, pVariant, _pszLDAPServer, &_Credentials ); BAIL_ON_FAILURE(hr); break; case VT_I4: hr = _pPropertyCache->unboundgetproperty( (DWORD)V_I4(pvVar), &dwSyntaxId, &dwPropStatus, &ldapSrcObjects ); BAIL_ON_FAILURE(hr); szPropName = _pPropertyCache->get_PropName( (DWORD)V_I4(pvVar) ); dwCtrlCode = MapPropCacheFlagToControlCode(dwPropStatus); hr = ConvertLdapValuesToVariant( szPropName, &ldapSrcObjects, dwSyntaxId, dwCtrlCode, pVariant, _pszLDAPServer, &_Credentials ); BAIL_ON_FAILURE(hr); break; case VT_I2: hr = _pPropertyCache->unboundgetproperty( (DWORD)V_I2(pvVar), &dwSyntaxId, &dwPropStatus, &ldapSrcObjects ); BAIL_ON_FAILURE(hr); szPropName = _pPropertyCache->get_PropName( (DWORD)V_I2(pvVar) ); dwCtrlCode = MapPropCacheFlagToControlCode(dwPropStatus); hr = ConvertLdapValuesToVariant( szPropName, &ldapSrcObjects, dwSyntaxId, dwCtrlCode, pVariant, _pszLDAPServer, &_Credentials ); BAIL_ON_FAILURE(hr); break; default: hr = E_FAIL; BAIL_ON_FAILURE(hr); } // // translate the Ldap objects to variants // error: LdapTypeFreeLdapObjects( &ldapSrcObjects ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPGenObject::PurgePropertyList() { _pPropertyCache->flushpropertycache(); RRETURN(S_OK); } DWORD MapPropCacheFlagToControlCode( DWORD dwPropStatus ) { DWORD dwADsCtrlCode = (DWORD) -1; switch (dwPropStatus) { case PROPERTY_INIT: // // 0 is not defined as any of the ADS_PROPERTY_ flags // use it to indicate that property is in init state // dwADsCtrlCode = 0; break; case PROPERTY_UPDATE: dwADsCtrlCode = ADS_PROPERTY_UPDATE; break; case PROPERTY_ADD: dwADsCtrlCode = ADS_PROPERTY_APPEND; break; case PROPERTY_DELETE: dwADsCtrlCode = ADS_PROPERTY_CLEAR; break; case PROPERTY_DELETE_VALUE: dwADsCtrlCode = ADS_PROPERTY_DELETE; break; default: // set to speical value to indicate unknow code dwADsCtrlCode = (DWORD) -1; break; } return dwADsCtrlCode; }