// UserInfo.cpp: implementation of the CUserInfo class. // Adapted from the CUserInfo class in the SBS Add User wizard // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include "UserInfo.h" #include #include #include #include // Must come before adshlp.h #include #include #include #include #ifndef ASSERT #define ASSERT assert #endif #ifndef WSTRING typedef std::wstring WSTRING; // Move to sbs6base.h #endif #ifndef CHECK_HR #define CHECK_HR( x ) CHECK_HR_RET( x, _hr ); #define CHECK_HR_RET( x, y ) \ { \ HRESULT _hr = x; \ if( FAILED(_hr) ) \ { \ ASSERT( !_T("CHECK_HR_RET() failed. calling TRACE() with HRESULT and statement") ); \ /*::AfxTrace( _T("CHECK_HR_RET() failed, hr == [%d], statement = [%s], returning [%s]"), _hr, #x, #y );*/ \ return y; \ } \ } #endif // CHECK_HR // Defines for account flags. #define PASSWD_NOCHANGE 0x01 #define PASSWD_CANCHANGE 0x02 #define PASSWD_MUSTCHANGE 0x04 #define ACCOUNT_DISABLED 0x10 // **************************************************************************** // CUserInfo // **************************************************************************** // ---------------------------------------------------------------------------- // CUserInfo() // // Constructor. // ---------------------------------------------------------------------------- CUserInfo::CUserInfo() { m_dwAccountOptions = 0; m_bCreateMB = TRUE; // Store the SBS Server Name TCHAR szServer[MAX_PATH+1] = {0}; DWORD dwLen = MAX_PATH; GetComputerName(szServer, &dwLen); m_csSBSServer = szServer; // Get domain controller name PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL; HRESULT hr = DsGetDcName (NULL, NULL, NULL, NULL, DS_DIRECTORY_SERVICE_REQUIRED, &DomainControllerInfo); ASSERT(hr == S_OK && "DsGetDcName failed"); // cache the domain if ( DomainControllerInfo ) { m_csDomainName = DomainControllerInfo->DomainName; // free memory NetApiBufferFree (DomainControllerInfo); } // convert from '.' separated names to Dc=xxx,DC=yyy,... format WSTRING tmpDomain = m_csDomainName; while ( !tmpDomain.empty() ) { int nPos = tmpDomain.find(_T(".")); if ( nPos == WSTRING::npos ) { nPos = tmpDomain.length(); } if ( !m_csFQDomainName.empty() ) { m_csFQDomainName += _T(","); } m_csFQDomainName += _T("DC="); m_csFQDomainName += tmpDomain.substr( 0, nPos ); tmpDomain.erase( 0, nPos + 1 ); } } CUserInfo::~CUserInfo() { for ( int i = 0 ; i < m_csPasswd.length() ; i++ ) m_csPasswd[i] = '\0' ; if ( 0 < m_csPasswd.length()) m_csPasswd[0] = NULL ; // Make sure that the last operation on the string isn't the origonal assignment. // The compiler will optimize the last statement out // if it is an assignment that doesn't get used. } // ---------------------------------------------------------------------------- // CreateAccount() // // Makes a new user account in the Active Directory. // ---------------------------------------------------------------------------- HRESULT CUserInfo::CreateAccount() { HRESULT hr = S_OK; BOOL bRO = TRUE; _bstr_t _bstr; _bstr_t _bstrClass; _bstr_t _bstrProperty; _variant_t _vTmpVal; CComPtr spADsContainer = NULL; // Bind to the container. WSTRING csLdapUserOU; if ( _tcsstr( m_csUserOU.c_str(), L"LDAP://") ) csLdapUserOU = m_csUserOU.c_str(); else { if ( m_csUserOU.empty() ) { csLdapUserOU = L"LDAP://CN=Users," + m_csFQDomainName; m_csUserOU = csLdapUserOU; } else csLdapUserOU = L"LDAP://" + m_csUserOU; } hr = ::ADsGetObject( csLdapUserOU.c_str(), IID_IADsContainer, (VOID**) &spADsContainer ); if ( FAILED(hr) ) return(hr); // Create the user account. CComPtr spDisp = NULL; _bstr = m_csUserCN.c_str(); _bstrClass = L"user"; hr = spADsContainer->Create( _bstrClass, _bstr, &spDisp ); if ( FAILED(hr) ) { ASSERT(FALSE); return(hr); } // m_pCmt->m_csADName = L"LDAP://"; // m_pCmt->m_csADName += m_csUserCN; // m_pCmt->m_csADName += L","; // m_pCmt->m_csADName += (LPCTSTR)csLdapUserOU+7; // Use this new account and set it's properties (e.g. first name, home folder, etc.). CComQIPtr spADsUserObj(spDisp); if ( !spADsUserObj ) { ASSERT(FALSE); return(E_FAIL); } TCHAR szTmp[MAX_PATH*4] = {0}; LdapToDCName( csLdapUserOU.c_str(), szTmp, (MAX_PATH*4)); WSTRING csUserPrincName = m_csUserName; csUserPrincName += L"@"; csUserPrincName += szTmp; EmailAddr = csUserPrincName; // Username: _vTmpVal.Clear(); _vTmpVal = ( csUserPrincName.c_str() ); _bstrProperty = L"userPrincipalName"; if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) ) { ASSERT(FALSE); return(E_FAIL); } // Pre-Win2000 username: _vTmpVal.Clear(); _vTmpVal = ( _tcslen( m_csUserNamePre2k.c_str() ) ? m_csUserNamePre2k.c_str() : m_csUserName.c_str() ); _bstrProperty = L"sAMAccountName"; if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) ) { ASSERT(FALSE); return(E_FAIL); } // Use UserName instead _bstr = m_csUserName.c_str(); if ( FAILED( spADsUserObj->put_FullName( _bstr ))) { ASSERT(FALSE); return(E_FAIL); } // Display Name _vTmpVal.Clear(); _vTmpVal = m_csUserName.c_str(); _bstrProperty = L"displayName"; if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) ) { ASSERT(FALSE); return(E_FAIL); } // Commit this information to the AD. CHECK_HR( spADsUserObj->SetInfo() ); // Password expired? _vTmpVal.Clear(); V_VT( &_vTmpVal ) = VT_I4; V_I4( &_vTmpVal ) = (m_dwAccountOptions & PASSWD_MUSTCHANGE) ? 0 : -1; _bstrProperty = L"pwdLastSet"; if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) ) { ASSERT(FALSE); } // Account disabled? _vTmpVal.Clear(); _bstrProperty = L"userAccountControl"; if ( FAILED(spADsUserObj->Get( _bstrProperty, &_vTmpVal )) ) { ASSERT(FALSE); } else { _vTmpVal.lVal &= ~UF_PASSWD_NOTREQD; // Make passwd required. if ( m_dwAccountOptions & ACCOUNT_DISABLED ) // Do we want to disable the account? _vTmpVal.lVal |= UF_ACCOUNTDISABLE; else _vTmpVal.lVal &= ~UF_ACCOUNTDISABLE; // ..not disable the account. if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) ) { ASSERT(FALSE); } } // Set the password. hr = SetPasswd(); if ( FAILED( hr )) { // m_pCmt->SetErrorResults(ERROR_PASSWORD); _vTmpVal.Clear(); _bstrProperty = L"userAccountControl"; if ( FAILED(spADsUserObj->Get( _bstrProperty, &_vTmpVal )) ) { ASSERT(FALSE); } else { _vTmpVal.lVal &= ~UF_PASSWD_NOTREQD; // Make passwd required. _vTmpVal.lVal |= UF_ACCOUNTDISABLE; // Disable the account. if ( FAILED(spADsUserObj->Put( _bstrProperty, _vTmpVal )) ) { ASSERT(FALSE); } } // Commit this information to the AD. if ( FAILED( spADsUserObj->SetInfo() )) { ASSERT(FALSE); } return hr; } // Commit this information to the AD. hr = spADsUserObj->SetInfo(); if ( FAILED( hr )) { ASSERT(FALSE); } return hr; } // ---------------------------------------------------------------------------- // CreateMailbox() // // Makes a new exchange mailbox for the user. // ---------------------------------------------------------------------------- HRESULT CUserInfo::CreateMailbox() { HRESULT hr = S_OK; CComVariant _vTmpVal; CComPtr spADsContainer = NULL; _bstr_t _bstr; _bstr_t _bstrClass; if ( 0 == m_csEXHomeMDB.length() ) { hr = GetMDBPath( m_csEXHomeMDB ); if FAILED( hr ) return hr; } // Do we even need to run? if ( 0 == m_csEXHomeMDB.length() || 0 == m_csEXAlias.length() ) return(hr); // Bind to the container. WSTRING csLdapUserOU = L"LDAP://"; if ( _tcsstr( m_csUserOU.c_str(), L"LDAP://") ) csLdapUserOU = m_csUserOU.c_str(); else { if ( m_csUserOU.empty() ) { csLdapUserOU = L"LDAP://CN=Users," + m_csFQDomainName; m_csUserOU = csLdapUserOU; } else csLdapUserOU = L"LDAP://" + m_csUserOU; } hr = ::ADsGetObject( csLdapUserOU.c_str(), IID_IADsContainer, (VOID**) &spADsContainer ); if ( FAILED(hr) || !spADsContainer ) { ASSERT(FALSE); return(hr); } // Open the user account. CComPtr spDisp = NULL; _bstr = m_csUserCN.c_str(); _bstrClass = L"user"; hr = spADsContainer->GetObject( _bstrClass, _bstr, &spDisp ); // if ( !spDisp ) // If the user doesn't exist, create it. // hr = spADsContainer->Create( L"user", (LPWSTR)(LPCWSTR)m_csUserCN, &spDisp ); // return(hr); // Let's just return.. maybe we'll want to change to create later? if ( !spDisp ) // Was there a problem getting the account (either existing or new)? { ASSERT(FALSE); return(hr); } // Get a handle on the user. CComPtr spADsUserObj; hr = spDisp->QueryInterface ( __uuidof(IADsUser), (void**)&spADsUserObj ); if ( FAILED(hr) || !spADsUserObj ) // Was there a problem getting the user object? { ASSERT(FALSE); return(hr); } // Get the interface needed to deal with the mailbox. CComPtr spMailboxStore; hr = spADsUserObj->QueryInterface ( __uuidof(IMailboxStore), (void**)&spMailboxStore ); if ( FAILED(hr) || !spMailboxStore ) // Was there a problem getting the mailbox store interface? { ASSERT(FALSE); return(hr); } // if ( SUCCEEDED( hr ) ) // { // Need to set the mailNickname first otherwise the alias will always be manufactured from the name. // m_csEXAlias = CreateEmailName( m_csEXAlias ); // m_csEXAlias.TrimLeft(); // m_csEXAlias.TrimRight(); // if ( 0 == m_csEXAlias.length( ) ) // { // m_csEXAlias.LoadString( IDS_NOLOC_USEREMAILALIAS ); // } // // CComBSTR bszTmp = (LPCTSTR) m_csEXAlias; // VARIANT v; // // VariantInit( &v ); // V_VT( &v ) = VT_BSTR; // V_BSTR( &v ) = bszTmp; // hr = spADsUserObj->Put( L"mailNickname", v ); // } if ( SUCCEEDED( hr ) ) { // Create the mailbox. WSTRING csLdapHomeMDB = L"LDAP://"; csLdapHomeMDB += m_csEXHomeMDB; _bstr = csLdapHomeMDB.c_str(); hr = spMailboxStore->CreateMailbox( _bstr ); if ( hr == S_OK ) // If it's a new mailbox, set the info. { hr = spADsUserObj->SetInfo(); } if ( HRESULT_CODE(hr) == ERROR_OBJECT_ALREADY_EXISTS ) // If it already exists, just move on. { hr = S_OK; } } if ( FAILED(hr) ) // Was there a problem? { ASSERT(FALSE); return(hr); } return(hr); } // ---------------------------------------------------------------------------- // SetPasswd() // ---------------------------------------------------------------------------- HRESULT CUserInfo::SetPasswd() { HRESULT hr = S_OK; WSTRING csUser = _T("LDAP://"); if ( _tcsstr( m_csUserOU.c_str(), _T("LDAP://")) ) { csUser += m_csUserCN; csUser += _T(","); csUser += m_csUserOU.c_str() + 7; } else { csUser += m_csUserCN; csUser += _T(","); csUser += m_csUserOU.c_str(); } // Now csUser is something like "LDAP://CN=JohnDoe,DC=Blah" CComPtr spDS = NULL; hr = ::ADsGetObject( csUser.c_str(), IID_IADsUser, (void**)&spDS ); CHECK_HR(hr); // Set the password. if ( m_csPasswd.length() ) // Only if there IS a passwd! { CComBSTR bszPasswd = m_csPasswd.c_str(); hr = spDS->SetPassword(bszPasswd); CHECK_HR(hr); } // Allow change? if ( m_dwAccountOptions & PASSWD_NOCHANGE ) { CComVariant vaTmpVal; CComBSTR bstrProp = _T("UserFlags"); vaTmpVal.Clear(); hr = spDS->Get( bstrProp, &vaTmpVal ); CHECK_HR(hr); vaTmpVal.lVal |= UF_PASSWD_CANT_CHANGE; hr = spDS->Put( bstrProp, vaTmpVal ); CHECK_HR(hr); } // SetInfo only if we actually changed anything. if ( ( m_csPasswd.length()) || // Did we mess with the passwd? ( m_dwAccountOptions & PASSWD_NOCHANGE ) ) // Did we make it unable to change? { hr = spDS->SetInfo(); // If either, then set the new info. } return hr; } ///////////////////////////////////////////////////////////////////////////////////// // SetUserName // Set the user name and dependant member variables (m_csUserCN, m_csEXAlias) ///////////////////////////////////////////////////////////////////////////////////// HRESULT CUserInfo::SetUserName( LPCTSTR szUserName ) { m_csUserName = szUserName; m_csUserCN = L"CN=" + m_csUserName; m_csEXAlias = szUserName; return S_OK; } ///////////////////////////////////////////////////////////////////////////////////// // SetPassword // Set the Password ///////////////////////////////////////////////////////////////////////////////////// HRESULT CUserInfo::SetPassword( LPCTSTR szPassword ) { m_csPasswd = szPassword; return S_OK; }