/*++ Copyright (C) Microsoft Corporation, 1996 - 1998 All rights reserved. Module Name: dsinterf.hxx Abstract: Directory service interface Author: Steve Kiraly (SteveKi) 09-Sept-1996 Revision History: --*/ #include "precomp.hxx" #pragma hdrstop #include "dsinterf.hxx" /*++ Name: TDirectoryService constructor. Description: TDirectoryService is a helper class for encapsulating DS related functions. Arguments: None. Return Value: Nothing, use bValid for class state check. --*/ TDirectoryService:: TDirectoryService( VOID ) : _bValid( FALSE ) { DBGMSG( DBG_TRACE, ( "TDirectoryService::ctor\n" ) ); // // We must initialize OLE for in each thread. In this // case we assume the user of this class only creates // one per thread. // HRESULT hr = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); if( hr == S_OK || hr == S_FALSE ) { _bValid = TRUE; } } /*++ Name: ~TDirectoryService destructor Description: Destructs this class, all clean up code should be placed here. Arguments: None. Return Value: Nothing. --*/ TDirectoryService:: ~TDirectoryService( VOID ) { DBGMSG( DBG_TRACE, ( "TDirectoryService::dtor.\n" ) ); // // Coinitlize was only called if we had // a valid object. // if( _bValid ) { // // Balance the CoInitialize. // CoUninitialize(); } } /*++ Name: bValid Description: Indicates if the class is valid. Arguments: None. Return Value: TRUE class is valid, FALSE class is in an invalid state. --*/ BOOL TDirectoryService:: bValid( VOID ) { DBGMSG( DBG_TRACE, ( "TDirectoryService::bValid.\n" ) ); // // This class is always valid. // return TRUE; } /*++ Name: bGetDirectoryName Description: Retrives the directory display name from the shell, i.e. ds folder. Arguments: strName - refrence to string object where to return directory display name. Return Value: TRUE name returne, FALSE error occurred. --*/ BOOL TDirectoryService:: bGetDirectoryName( IN TString &strName ) { TStatusB bStatus; if( _strDirectoryName.bEmpty() ) { TLibrary Lib( TEXT("dsuiext.dll") ); if( VALID_OBJ( Lib ) ) { typedef HRESULT (WINAPI *PF_FORMATDIRECTORYNAME)( LPTSTR *, PVOID, UINT ); typedef VOID (WINAPI *PF_LOCALFREESTRING)( LPTSTR * ); PF_FORMATDIRECTORYNAME pfFormatDirectoryName = (PF_FORMATDIRECTORYNAME)Lib.pfnGetProc( 578 ); PF_LOCALFREESTRING pfLocalFreeString = (PF_LOCALFREESTRING)Lib.pfnGetProc( 542 ); if( pfFormatDirectoryName && pfLocalFreeString ) { HRESULT hr; LPTSTR pName = NULL; hr = pfFormatDirectoryName( &pName, NULL, 0 ); if ( SUCCEEDED(hr) ) { bStatus DBGCHK = _strDirectoryName.bUpdate( pName ); pfLocalFreeString(&pName); } } } } // // If the directory name is empty indicate we cannot // get the directory name. // if( _strDirectoryName.bEmpty() ) { bStatus DBGNOCHK = FALSE; } else { // // Return the directory name. // bStatus DBGCHK = strName.bUpdate( _strDirectoryName ); } return bStatus; } /*++ Name: bIsDsAvailable Description: Indicates if the directory service is available either for the user or from the machines perspective. Arguments: pName - pointer to a machine name, i.e. this call is remoteable. bForUser - TRUE indicate it is a user check, FALSE for machine. Return Value: TRUE if directory is available, otherwize false. --*/ BOOL TDirectoryService:: bIsDsAvailable( IN LPCTSTR pName, IN BOOL bForUser ) { BOOL bReturn = FALSE; DWORD dwStatus = ERROR_SUCCESS; DWORD dwAccess = SERVER_READ; DWORD dwDsStatus = 0; HANDLE hServer = NULL; // // Open the server for read access. // dwStatus = TPrinter::sOpenPrinter( pName, &dwAccess, &hServer ); // // If server handle opened. // if( dwStatus == ERROR_SUCCESS ) { DWORD dwNeeded = 0; DWORD dwDsStatusType = REG_DWORD; if( dwStatus == ERROR_SUCCESS ) { // // If the request for user or for machine. // LPTSTR pszKey = bForUser ? SPLREG_DS_PRESENT_FOR_USER : SPLREG_DS_PRESENT; // // Get the printer data key which indicates the DS is available. // dwStatus = GetPrinterData( hServer, pszKey, &dwDsStatusType, (PBYTE)&dwDsStatus, sizeof( dwDsStatus ), &dwNeeded ); } if( dwStatus == ERROR_SUCCESS ) { bReturn = dwDsStatus; } } // // Close the server handle. // if( hServer ) { ClosePrinter( hServer ); } return bReturn; } /*++ Name: bIsDsAvailable Description: Indicates if the directory service is available either for the using the same check the shell uses. Arguments: None. Return Value: TRUE if directory is available, otherwize false. --*/ BOOL TDirectoryService:: bIsDsAvailable( VOID ) { DBGMSG( DBG_TRACE, ( "TDirectoryService::bIsDsAvailble - ShowDirectoryUI.\n" ) ); BOOL bStatus = FALSE; TLibrary Lib( TEXT("dsuiext.dll") ); if( VALID_OBJ( Lib ) ) { typedef BOOL (WINAPI *PF_SHOWDIRECTORYUI)( VOID ); PF_SHOWDIRECTORYUI pfShowDirectoryUI = (PF_SHOWDIRECTORYUI)Lib.pfnGetProc( 577 ); if( pfShowDirectoryUI ) { bStatus = pfShowDirectoryUI(); } } return bStatus; } HRESULT TDirectoryService:: ADsGetObject( IN LPWSTR lpszPathName, IN REFIID riid, IN OUT VOID **ppObject ) { return ::ADsGetObject( lpszPathName, riid, ppObject ); } HRESULT TDirectoryService:: ADsBuildEnumerator( IADsContainer *pADsContainer, IEnumVARIANT **ppEnumVariant ) { return ::ADsBuildEnumerator( pADsContainer, ppEnumVariant); } HRESULT TDirectoryService:: ADsFreeEnumerator( IEnumVARIANT *pEnumVariant ) { return ::ADsFreeEnumerator( pEnumVariant ); } HRESULT TDirectoryService:: ADsEnumerateNext( IEnumVARIANT *pEnumVariant, ULONG cElements, VARIANT FAR *pvar, ULONG FAR *pcElementsFetched ) { return ::ADsEnumerateNext( pEnumVariant, cElements, pvar, pcElementsFetched); } /*++ Name: Get Description: This routine gets a string value from the specified DS object using the specified property. A copy of the string is made and placed into the provided string object. Arguments: pDsObject - pointer to the DS object interface pszPropertyName - DS object property name strString - refrence to string object where to return string Return Value: TRUE string put successfully, FALSE error occurred. --*/ BOOL TDirectoryService:: Get( IN IADs *pDsObject, IN LPCTSTR pszPropertyName, IN TString &strString ) { SPLASSERT( pDsObject ); SPLASSERT( pszPropertyName ); VARIANT var; VariantInit( &var ); HRESULT hr = pDsObject->Get( const_cast( pszPropertyName ), &var ); if( SUCCEEDED( hr ) && ( var.vt == VT_BSTR ) ) { hr = strString.bUpdate( V_BSTR(&var) ) ? S_OK : E_FAIL; } VariantClear (&var); return SUCCEEDED( hr ); } /*++ Name: Put Description: This routine puts a string value on the specified DS object for the specified property. This routine does not make a copy of the string but places it into a variant that is inturn passed to Ads::Put method. Arguments: pDsObject - pointer to the DS object interface pszPropertyName - DS object property name pszString - string value to put Return Value: TRUE string put successfully, FALSE error occurred. --*/ BOOL TDirectoryService:: Put( IN IADs *pDsObject, IN LPCTSTR pszPropertyName, IN LPCTSTR pszString ) { SPLASSERT( pDsObject ); SPLASSERT( pszPropertyName ); VARIANT var; HRESULT hr; VariantInit (&var); if( !pszString || !*pszString ) { hr = pDsObject->PutEx( ADS_PROPERTY_CLEAR, const_cast( pszPropertyName ), var ); } else { var.vt = VT_BSTR; var.bstrVal = const_cast( pszString ); hr = pDsObject->Put( const_cast( pszPropertyName ), var ); } if( SUCCEEDED( hr ) ) { hr = pDsObject->SetInfo(); if( FAILED(hr) && ( !pszString || !*pszString ) ) { // // From our perspective if we fail to delete a property because it does not exist // the operation actually succeeded. // if( HRESULT_CODE(hr) == ERROR_DS_NO_ATTRIBUTE_OR_VALUE ) { hr = S_OK; } } } var.bstrVal = NULL; VariantClear (&var); return SUCCEEDED(hr); } /*++ Name: TReadStringProperty Description: This routine reads the specified string using the specified path and property name from the DS. Arguments: strString - refrence to class where to returned read string. Return Value: TRUE string was read, FALSE error occurred. Notes: --*/ BOOL TDirectoryService:: ReadStringProperty( IN LPCTSTR pszPath, IN LPCTSTR pszProperty, IN OUT TString &strString ) { IADs *pADs; HRESULT hStatus; hStatus = ADsGetObject( const_cast( pszPath ), IID_IADs, (void **)&pADs); if (SUCCEEDED(hStatus)) { VARIANT var; VariantInit(&var); hStatus = pADs->Get( const_cast( pszProperty ), &var); if (SUCCEEDED(hStatus)) { hStatus = strString.bUpdate( V_BSTR(&var) ) ? S_OK : E_FAIL; } VariantClear (&var); pADs->Release(); } if (FAILED(hStatus)) { DBGMSG( DBG_TRACE, ("ReadStringProperty Path " TSTR " Property " TSTR " failed %x\n", pszPath, pszProperty, hStatus ) ); } return SUCCEEDED(hStatus); } /*++ Name: GetConfigurationContainer Description: This routine figures out the and returnes the path of the longged on DS's configuration containter. Arguments: strConfig - Refrence to a string where to return the configuration string. Return Value: TRUE configuration containter returned. FALSE error occurred. Notes: --*/ BOOL TDirectoryService:: GetConfigurationContainer( IN OUT TString &strConfig ) { TStatusB bStatus; // // If we have not already read the configuration container // then read it now. // if (_strConfigurationContainer.bEmpty()) { TString strRootDSE; TString strLDAPPrefix; bStatus DBGCHK = GetLDAPPrefix( strLDAPPrefix ) && strRootDSE.bCat( strLDAPPrefix ) && strRootDSE.bCat( gszRootDSE ); if (bStatus) { bStatus DBGCHK = ReadStringProperty( strRootDSE, gszConfigurationNameingContext, _strConfigurationContainer ); } } bStatus DBGCHK = strConfig.bUpdate( _strConfigurationContainer ); DBGMSG( DBG_TRACE, ( "Configuration Container " TSTR ".\n", (LPCTSTR)strConfig ) ); return bStatus; } /*++ Name: GetDsName Description: Returns a the DS UNC name that can be used in the call to AddressToSiteNames. Arguments: strDsName - refrence to string where to return the Ds name. Return Value: TRUE success, FALSE error occurred. Notes: --*/ BOOL TDirectoryService:: GetDsName( IN TString &strDsName ) { TString strDomainName; TStatusB bStatus; bStatus DBGCHK = GetDomainName( strDomainName ); if (bStatus) { bStatus DBGCHK = strDsName.bUpdate( gszLeadingSlashes ) && strDsName.bCat( strDomainName ); } return bStatus; } /*++ Name: GetLDAPPrefix Description: Returns the correct LDAP prefix using fully qualified machine account domain name (example "LDAP://ntdev.microsoft.com/" Arguments: strLDAPPrefix - where to return The LDAP prefix (domain relative or fully qualified) Return Value: TRUE on success, FALSE othewise Notes: --*/ BOOL TDirectoryService:: GetLDAPPrefix( OUT TString &strLDAPPrefix ) { TStatusB bStatus; bStatus DBGNOCHK = TRUE; if( !_strLDAPPrefix.uLen( ) ) { TString strRootDSE; TString strConfig; bStatus DBGCHK = strRootDSE.bCat( gszLdapPrefix ) && strRootDSE.bCat( gszRootDSE ); if( bStatus ) { // // We need to use fully qualified domain LDAP address // TString strDomainName; bStatus DBGCHK = GetDomainName( strDomainName ); if( bStatus ) { bStatus DBGCHK = _strLDAPPrefix.bUpdate( NULL ) && _strLDAPPrefix.bCat( gszLdapPrefix ) && _strLDAPPrefix.bCat( strDomainName ) && _strLDAPPrefix.bCat( gszSlash ); } } } if( !_strLDAPPrefix.uLen( ) || !bStatus ) { bStatus DBGCHK = _strLDAPPrefix.bUpdate( gszLdapPrefix ); } if( bStatus ) { bStatus DBGCHK = strLDAPPrefix.bUpdate( _strLDAPPrefix ); } return bStatus; } /*++ Name: GetLDAPPrefixPerUser Description: Returns the correct LDAP prefix using fully qualified machine account domain name (example "LDAP://ntdev.microsoft.com/" if the user domain (the domain in which the user is logged on) doesn't have a DS, if the user domain has a DS then we are using domain relative prefix (i.e. "LDAP://"). Arguments: strLDAPPrefix - where to return The LDAP prefix (domain relative or fully qualified) Return Value: TRUE on success, FALSE othewise Notes: --*/ BOOL TDirectoryService:: GetLDAPPrefixPerUser( OUT TString &strLDAPPrefix ) { TStatusB bStatus; bStatus DBGNOCHK = TRUE; if( !_strLDAPPrefixPerUser.uLen( ) ) { TString strRootDSE; TString strConfig; bStatus DBGCHK = strRootDSE.bCat( gszLdapPrefix ) && strRootDSE.bCat( gszRootDSE ); if( bStatus ) { bStatus DBGCHK = ReadStringProperty( strRootDSE, gszConfigurationNameingContext, strConfig ); if( bStatus ) { // // We can use domain relative LDAP address // bStatus DBGCHK = _strLDAPPrefixPerUser.bUpdate( gszLdapPrefix ); } else { // // We need to use fully qualified domain LDAP address // bStatus DBGCHK = GetLDAPPrefix (_strLDAPPrefixPerUser); } } } if( !_strLDAPPrefixPerUser.uLen( ) || !bStatus ) { bStatus DBGCHK = _strLDAPPrefixPerUser.bUpdate( gszLdapPrefix ); } if( bStatus ) { bStatus DBGCHK = strLDAPPrefix.bUpdate( _strLDAPPrefixPerUser ); } return bStatus; }