#include "precomp.h" #include "resource.h" #include "pfnwldap.h" #include "conf.h" #include "cr.h" #include #include #include "NmLdap.h" #include "version.h" #include "call.h" #include "confapi.h" #include "confutil.h" #include "dirutil.h" // One global instance CNmLDAP* g_pLDAP = NULL; CPing* g_pPing = NULL; // This flag indicates weather or not we have loaded the LDAP dll ( WLDAP::Init() ) /*static*/ bool CNmLDAP::ms_bLdapDLLLoaded = false; //////////////////////////////////////////////////////////////////////////////////////// // Helper Macros // the ldap_modify function takes an LDAPMod** type // If it took an array of LDAPMod ( an LDAPMod* type ) // Then life would be easier for us to make static lists // Unfortunately, this is not how it works, so we have macros // to pretty things up ( and hopefully not to confuse anyone ) // This is to declare an LDAPMod with one val #define DECLARE_LDAP_MOD( t, x, y )\ LPTSTR aVals_##x[] = { y, NULL };\ LDAPMod LDAPMod_##x = { t, _T(#x), aVals_##x } // This is to declare an LDAPMod with 2 vals #define DECLARE_LDAP_MOD2( t, x, y1, y2 )\ LPTSTR aVals_##x[] = { y1, y2, NULL };\ LDAPMod LDAPMod_##x = { t, _T(#x), aVals_##x } #define LDAP_MOD_ENTRY( x ) &LDAPMod_##x #define LDAP_MOD_ADD_ENTRY( x ) &LDAPMod_add_##x //////////////////////////////////////////////////////////////////////////////////////// // Some helpful deffinitions #define LDAP_REFRESH_BASE_DN _T("objectClass=RTPerson") #define RESOLVE_USER_SEARCH_FILTER _T("(&(objectClass=RTPerson)(cn=%s))") //NOTE: The LDAP_REFRESH_FILTER... notice the sttl=10... aparently, this is how you request the // server to restart the TTL countdown... the sttl= value can be anything BUT '*' ( the value you would // expecte it to be... ) This is a link to the "Dynamir Ldap Extension RFC" if you want more info // http://www.critical-angle.com/ldapworld/draft-ietf-asid-ldapv3ext-04.txt #define LDAP_REFRESH_FILTER _T("(&(objectClass=RTPerson)(cn=%s)(sttl=10))") #define LOGIN_USER_FILTER _T("c=-,o=Microsoft,cn=%s,objectClass=RTPerson") #define IP_ADDRESS_ATTR_NAME _T("sipaddress") #define STTL_ATTR_NAME _T("sttl") #define CNmLDAP_WCNAME _T("CNmLDAP_WCNAME") #define LDAP_MODOP_ADDAPP _T("0") #define LDAP_MODOP_DELETEAPP _T("1") #define LDAP_MODOP_MODIFYUSER _T("2") #define LDAP_MODOP_MODIFYAPP _T("3") #define LDAP_ATTR_FALSE _T("0") #define LDAP_ATTR_TRUE _T("1") #define LDAP_ATTR_BUSINESS _T("2") // We are not using the country name anymore, but we can't leave the country // field blank ( empty string ), it is just the way that SiteServer is written... #define LDAP_ATTR_DUMMY_COUNTRYNAME _T("-") #define INVALID_MSG_ID ((ULONG) -1) // same as ldap_****() #define LDAP_RESULT_ERROR ((ULONG) -1) #define LDAP_RESULT_TIMEOUT 0 // When the bind helper thread function gets the result to bind_s function // it passes it to this thread via PostMessage( m_hWndHidder, WM_USER_BIND_COMPLETE, ret, err ) #define WM_USER_BIND_COMPLETE (WM_APP + 1) // wParam = Return code from bind_s, lParam = GetLastError() #define WM_NEED_RELOGON (WM_APP + 2) CNmLDAP::CNmLDAP() : m_State(Uninitialized), m_pKeepAlive(NULL), m_hWndHidden(NULL), m_pLdap(NULL), m_ResultPollTimer(0), m_LogoffTimer(0), m_uMsgID(INVALID_MSG_ID), m_CurrentOp(Op_NoOp), m_bVisible(true), m_bDirty(false), m_bRefreshAfterBindCompletes(false), m_bLogoffAfterBindCompletes(false), m_bSendInCallAttrWhenDone(false), m_bInCall(false), m_hEventWaitForLogoffDone(NULL), m_hBindThread(NULL), m_iPort(DEFAULT_LDAP_PORT), m_bAudioHardware(false), m_bVideoHardware(false) { DBGENTRY(CNmLDAP::CNmLDAP); SetLoggedOn( false ); DBGEXIT(CNmLDAP::CNmLDAP); } CNmLDAP::~CNmLDAP() { DBGENTRY(CNmLDAP::~CNmLDAP); if (NULL != m_pKeepAlive) { m_pKeepAlive->End(TRUE); // synchronous end m_pKeepAlive = NULL; } if((m_State != Idle) && (m_State != Uninitialized)) { // Make sure that someone at least has already called Logoff() ASSERT((LoggingOff == m_State) || (Op_Logoff == m_CurrentOp)); // We are logging off, but we would like to wait a while to make sure that // all of our async operations are completed... We don't want to wait too long, // Though... Basically we set a timer to pass a message to us... if we get that // message, we signal m_hEventWaitForLogoffDone.... if any of the async operations // complete during this time, they will also signal this event....We make sure to // hawe a message loop ( AtlWaitWithWessageLoop ) so that our wndproc will be called m_hEventWaitForLogoffDone = CreateEvent(NULL,TRUE,FALSE,NULL); if(m_hEventWaitForLogoffDone) { m_LogoffTimer = ::SetTimer(m_hWndHidden, WaitForLogoffTimer, LOGOFF_WAIT_INTERVAL, NULL); if(m_LogoffTimer) { AtlWaitWithMessageLoop(m_hEventWaitForLogoffDone); } CloseHandle(m_hEventWaitForLogoffDone); m_hEventWaitForLogoffDone = NULL; } else { ERROR_OUT(("Create Event failed")); } if( Binding == m_State ) { WARNING_OUT(("Aborting an ldap_bind because we are closing down in ~CNmLDAP")); TerminateThread(m_hBindThread, 0); CloseHandle(m_hBindThread); } else if(Idle != m_State) { WARNING_OUT(("Aborting pending LDAP operation in ~CNmLDAP")); _AbandonAllAndSetToIdle(); } } if( NULL != m_hWndHidden ) { DestroyWindow( m_hWndHidden ); m_hWndHidden = NULL; } m_State = Uninitialized; SetLoggedOn( false ); DBGEXIT(CNmLDAP::~CNmLDAP); } HRESULT CNmLDAP::_bCallChangedHelper() { TRACE_OUT(("CNmLDAP::_bCallChangedHelper:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); HRESULT hr = S_OK; if( IsLoggedOn() || IsBusy() ) { if(LoggedIn == m_State) { // We simply have to bind and call ldap_modify m_CurrentOp = Op_Modifying_InCallAttr; hr = _BindAsync(); } else if((SettingAppInfo == m_State) || (ModifyingAttrs == m_State) || (AddingUser == m_State) ) { // the other states will be calling ldap_mod( user Attributes ) which will pick up // the change to m_bInCall. The states listed above are the ones that occur after // the user attributes have been sent to the server... There are also other states // that logoff, so they don't care about sending the update to the user attribute to the server m_bSendInCallAttrWhenDone = true; } } TRACE_OUT(("CNmLDAP::_bCallChangedHelper:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); return hr; } HRESULT CNmLDAP::OnCallStarted() { DBGENTRY(CNmLDAP::OnCallStarted); TRACE_OUT(("CNmLDAP::OnCallStarted:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); HRESULT hr = S_OK; ASSERT(ms_bLdapDLLLoaded); if( !m_bInCall ) { m_bInCall = true; hr = _bCallChangedHelper(); } TRACE_OUT(("CNmLDAP::OnCallStarted:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::OnCallStarted,hr); return hr; } HRESULT CNmLDAP::OnCallEnded() { DBGENTRY(CNmLDAP::OnCallEnded); TRACE_OUT(("CNmLDAP::OnCallEnded:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); HRESULT hr = S_OK; ASSERT(ms_bLdapDLLLoaded); if( m_bInCall ) { m_bInCall = false; hr = _bCallChangedHelper(); } TRACE_OUT(("CNmLDAP::OnCallEnded:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::OnCallEnded,hr); return hr; } bool CNmLDAP::IsLoggedOn() const { DBGENTRY(CNmLDAP::IsLoggedOn); TRACE_OUT(("CNmLDAP::IsLoggedOn:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); bool bRet = ( ( LoggedIn == m_State ) || ( ModifyingAttrs == m_State ) || ( Op_Refresh_Logoff == m_CurrentOp ) || ( Op_Modifying_InCallAttr == m_CurrentOp ) ); TRACE_OUT(("CNmLDAP::IsLoggedOn:exit(%d): m_State:%d: m_currentOp:%d", bRet, m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::IsLoggedOn); return bRet; } bool CNmLDAP::IsLoggingOn() const { DBGENTRY(CNmLDAP::IsLoggingOn); TRACE_OUT(("CNmLDAP::IsLoggingOn:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); bool bRet = ( ( AddingUser == m_State ) || ( UserAdded == m_State ) || ( SettingAppInfo == m_State ) || ( Op_Logon == m_CurrentOp ) ); TRACE_OUT(("CNmLDAP::IsLoggingOn:exit(%d): m_State:%d: m_currentOp:%d", bRet, m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::IsLoggingOn); return bRet; } bool CNmLDAP::IsBusy() const { DBGENTRY(CNmLDAP::IsBusy); TRACE_OUT(("CNmLDAP::IsBusy:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); bool bRet = !((LoggedIn == m_State) || ( Idle == m_State ) || (Uninitialized == m_State)); TRACE_OUT(("CNmLDAP::IsBusy:exit(%d): m_State:%d: m_currentOp:%d", bRet, m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::IsBusy); return bRet; } void CNmLDAP::_MakeStatusText(UINT uResID, LPTSTR psz, UINT cch) const { const TCHAR * const server = CDirectoryManager::get_displayName( m_strCurrentServer ); LPCTSTR pszFmt = RES2T(uResID); LPTSTR pszStr = reinterpret_cast(_alloca(lstrlen(pszFmt) + lstrlen(server) - 1)); if(pszStr) { wsprintf(pszStr, pszFmt, server); lstrcpyn(psz, pszStr, cch); } } HRESULT CNmLDAP::GetStatusText(LPTSTR psz, int cch, UINT *idIcon ) const { DBGENTRY(CNmLDAP::GetStatusText); UINT idDummy; if (NULL == idIcon) { // Just so we don't need to do switches all over the place idIcon = &idDummy; } HRESULT hr = S_OK; TRACE_OUT(("m_State = %d", m_State)); TRACE_OUT(("CNmLDAP::GetStatusText:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); if(psz) { if( cch > 1 ) { switch(m_State) { case LoggedIn: case ModifyingAttrs: *idIcon = IDI_NET; _MakeStatusText(ID_STATUS_LOGGEDON_FORMAT, psz, cch); break; case Binding: { switch(m_CurrentOp) { case Op_Logon: *idIcon = IDS_STATUS_WAITING; _MakeStatusText(ID_STATUS_LOGON_FORMAT, psz, cch); break; case Op_Modifying_InCallAttr: *idIcon = IDI_NET; _MakeStatusText(ID_STATUS_LOGGEDON_FORMAT, psz, cch); break; case Op_Refresh_Logoff: if( lstrcmpi(m_strServer,m_strCurrentServer)) { *idIcon = IDS_STATUS_WAITING; _MakeStatusText(ID_STATUS_LOGOFF_FORMAT, psz, cch); } else { *idIcon = IDI_NET; _MakeStatusText(ID_STATUS_LOGGEDON_FORMAT, psz, cch); } break; case Op_Logoff: *idIcon = IDI_NETGRAY; lstrcpyn(psz, RES2T(ID_STATUS_LOGGEDOFF), cch); break; } break; } case AddingUser: case UserAdded: case SettingAppInfo: *idIcon = IDS_STATUS_WAITING; _MakeStatusText(ID_STATUS_LOGON_FORMAT, psz, cch); break; case LoggingOff: *idIcon = IDS_STATUS_WAITING; _MakeStatusText(ID_STATUS_LOGOFF_FORMAT, psz, cch); break; case Bound: case Idle: case Uninitialized: *idIcon = IDI_NETGRAY; lstrcpyn(psz, RES2T(ID_STATUS_LOGGEDOFF), cch); break; default: ERROR_OUT(("Not a regognized state: %d", m_State)); break; } } else { hr = E_INVALIDARG; } } else { hr = E_POINTER; } TRACE_OUT(("CNmLDAP::GetStatusText:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::GetStatusText,hr); return hr; } HRESULT CNmLDAP::OnSettingsChanged() { DBGENTRY(CNmLDAP::OnSettingsChanged); TRACE_OUT(("CNmLDAP::OnSettingsChanged:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); HRESULT hr = S_OK; ASSERT(ms_bLdapDLLLoaded); if(S_OK == _GetUserSettingsFromRegistryAndGlobals()) { hr = OnReLogon(); } TRACE_OUT(("CNmLDAP::OnSettingsChanged:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::OnSettingsChanged,hr); return hr; } HRESULT CNmLDAP::OnReLogon(void) { HRESULT hr = S_OK; ASSERT(ms_bLdapDLLLoaded); m_bDirty = true; if(IsLoggedOn() || IsBusy()) { hr = _RefreshServer(); } return hr; } HRESULT CNmLDAP::_RefreshServer() { DBGENTRY(CNmLDAP::_RefreshServer); TRACE_OUT(("CNmLDAP::_RefreshServer:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); HRESULT hr = S_OK; ASSERT(ms_bLdapDLLLoaded); if( m_bDirty ) { if(Binding == m_State) { if(Op_Logon == m_CurrentOp) // we are logging on and haven't sent any user information yet { // Check to see if we have not changed the server name if( !lstrcmpi(m_strServer, CDirectoryManager::get_defaultServer() ) ) { // Simply update the user settings _GetUserSettingsFromRegistryAndGlobals(); } else { // Since the server has changed, we have to do a logoff(from old)/logon(to new) _InternalLogoff(true); } } else if(m_CurrentOp != Op_Logoff) { // If we are in the middle of a bind operation, which happens // in another thread, we just have to wait for it to complete // before we go any further m_bRefreshAfterBindCompletes = true; } } else if(Idle == m_State) { _GetUserSettingsFromRegistryAndGlobals(); LogonAsync(); } else { _InternalLogoff(true); } } TRACE_OUT(("CNmLDAP::_RefreshServer:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::_RefreshServer,hr); return hr; } void CNmLDAP::_AbandonAllAndSetState(eState new_state) { TRACE_OUT(("CNmLDAP::_AbandonAllAndSetState:enter: new_state=%d, m_State:%d: m_currentOp:%d", new_state, m_State, m_CurrentOp)); if(m_ResultPollTimer) { ::KillTimer(m_hWndHidden,m_ResultPollTimer); m_ResultPollTimer = 0; } if(m_pLdap) { if( INVALID_MSG_ID != m_uMsgID ) { WLDAP::ldap_abandon(m_pLdap, m_uMsgID); } WLDAP::ldap_unbind(m_pLdap); } m_pLdap = NULL; m_uMsgID = INVALID_MSG_ID; m_State = new_state; m_CurrentOp = Op_NoOp; SetLoggedOn( IsLoggedOn() ); TRACE_OUT(("CNmLDAP::_AbandonAllAndSetState:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); } HRESULT CNmLDAP::_InternalLogoff(bool bRefreshLogoff) { DBGENTRY(CNmLDAP::_InternalLogoff); TRACE_OUT(("CNmLDAP::_InternalLogoff:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); HRESULT hr = S_OK; ASSERT(ms_bLdapDLLLoaded); if( ( LoggingOff != m_State ) && ( Idle != m_State ) ) { if(bRefreshLogoff) { m_CurrentOp = Op_Refresh_Logoff; // If the server names are different, this means // that we should display "logging off" XXX if( lstrcmpi(m_strServer, m_strCurrentServer) ) { _OnLoggingOff(); } } else { _OnLoggingOff(); m_CurrentOp = Op_Logoff; } if( LoggedIn == m_State ) { hr = _BindAsync(); } else if( Binding == m_State ) { m_bLogoffAfterBindCompletes = true; } else if( (INVALID_MSG_ID != m_uMsgID ) && (NULL != m_pLdap )) { // Kill the timer ASSERT(m_ResultPollTimer); ::KillTimer(m_hWndHidden,m_ResultPollTimer); m_ResultPollTimer = 0; // Abandon the current op WLDAP::ldap_abandon(m_pLdap, m_uMsgID); m_uMsgID = INVALID_MSG_ID; m_State = Bound; SetLoggedOn( IsLoggedOn() ); } if(Bound == m_State) { ASSERT(m_pLdap); _DeleteUser(); } } TRACE_OUT(("CNmLDAP::_InternalLogoff:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::_InternalLogoff,hr); return hr; } HRESULT CNmLDAP::Logoff() { DBGENTRY(CNmLDAP::Logoff); TRACE_OUT(("CNmLDAP::Logoff:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); HRESULT hr = _InternalLogoff(false); TRACE_OUT(("CNmLDAP::Logoff:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::Logoff,hr); return hr; } HRESULT CNmLDAP::Initialize( HINSTANCE hInst ) { HRESULT hr = S_OK; DBGENTRY(CNmLDAP::Initialize); TRACE_OUT(("CNmLDAP::Initialize:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); ASSERT( Uninitialized == m_State ); if( ms_bLdapDLLLoaded || SUCCEEDED( hr = WLDAP::Init())) { ms_bLdapDLLLoaded = true; //////////////////////////////////////// // Initialize user data _GetUserSettingsFromRegistryAndGlobals(); //////////////////////////////////////// // Initialize hidden Window WNDCLASS wcHidden = { 0L, _sWndProc, 0, 0, hInst, NULL, NULL, NULL, NULL, CNmLDAP_WCNAME }; if(RegisterClass(&wcHidden)) { m_hWndHidden = ::CreateWindow( CNmLDAP_WCNAME, g_szEmpty, 0, 0, 0, 0, 0, NULL, NULL, hInst, this); if( m_hWndHidden ) { m_State = Idle; SetLoggedOn( IsLoggedOn() ); } else { hr = HRESULT_FROM_WIN32(GetLastError()); } } else { hr = HRESULT_FROM_WIN32(GetLastError()); } } else { ERROR_OUT(("WLDAP::Init failed ( could not load wldap32.dll? )")); } TRACE_OUT(("CNmLDAP::Initialize:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::Initialize,hr); return hr; } HRESULT CNmLDAP::_GetUserSettingsFromRegistryAndGlobals() { DBGENTRY(CNmLDAP::_GetUserSettingsFromRegistryAndGlobals); TRACE_OUT(("CNmLDAP::_GetUserSettingsFromRegistryAndGlobals:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); HRESULT hr = S_FALSE; // this would indicate that nothing changed RegEntry reULS(ISAPI_CLIENT_KEY, HKEY_CURRENT_USER); if(lstrcmpi(m_strSurName, reULS.GetString(ULS_REGKEY_LAST_NAME))) { hr = S_OK; m_strSurName = reULS.GetString(ULS_REGKEY_LAST_NAME); } if(lstrcmpi(m_strGivenName, reULS.GetString(ULS_REGKEY_FIRST_NAME))) { hr = S_OK; m_strGivenName = reULS.GetString(ULS_REGKEY_FIRST_NAME); } if(lstrcmpi(m_strEmailName, reULS.GetString(REGVAL_ULS_EMAIL_NAME))) { hr = S_OK; m_strEmailName = reULS.GetString(REGVAL_ULS_EMAIL_NAME); } if(lstrcmpi(m_strComment, reULS.GetString(ULS_REGKEY_COMMENTS))) { hr = S_OK; m_strComment = reULS.GetString(ULS_REGKEY_COMMENTS); } if(lstrcmpi(m_strLocation, reULS.GetString(ULS_REGKEY_LOCATION))) { hr = S_OK; m_strLocation = reULS.GetString(ULS_REGKEY_LOCATION); } if(lstrcmpi(m_strSecurityToken, reULS.GetString(ULS_REGKEY_CLIENT_ID))) { hr = S_OK; m_strSecurityToken = reULS.GetString(ULS_REGKEY_CLIENT_ID); } if(*m_strSecurityToken == '\0') { // The string was not found... // When we log onto the LDAP server, we pass a fairly unique ID // This is passed as the value to the ssecurity attribute // In the case that NetMeeting goes away without actually logging // off of the ILS server, when NetMeeting tries to log back on, // it will pass this unique ID... if the server has not closed the // session account for the user, the server uses this ID to "authenticate" // that we are the same user as the last session... TCHAR szNewClientID[MAX_PATH]; wsprintf(szNewClientID,"%lu", ::GetTickCount()); m_strSecurityToken = szNewClientID; reULS.SetValue(ULS_REGKEY_CLIENT_ID,szNewClientID); hr = S_OK; } if( lstrcmpi(m_strServer, CDirectoryManager::get_defaultServer())) { hr = S_OK; m_strServer = CDirectoryManager::get_defaultServer(); m_iPort = DEFAULT_LDAP_PORT; } if(m_bVisible != (!reULS.GetNumber(ULS_REGKEY_DONT_PUBLISH, REGVAL_ULS_DONT_PUBLISH_DEFAULT))) { m_bVisible = !reULS.GetNumber(ULS_REGKEY_DONT_PUBLISH, REGVAL_ULS_DONT_PUBLISH_DEFAULT); hr = S_OK; } if(m_bAudioHardware != (0 != (g_uMediaCaps & CAPFLAG_SEND_AUDIO))) { m_bAudioHardware = (0 != (g_uMediaCaps & CAPFLAG_SEND_AUDIO)); hr = S_OK; } if(m_bVideoHardware != (0 != (g_uMediaCaps & CAPFLAG_SEND_VIDEO))) { m_bVideoHardware = (0 != (g_uMediaCaps & CAPFLAG_SEND_VIDEO)); hr = S_OK; } TRACE_OUT(("CNmLDAP::_GetUserSettingsFromRegistryAndGlobals:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::_GetUserSettingsFromRegistryAndGlobals,hr); return hr; } HRESULT CNmLDAP::LogonAsync( LPCTSTR pcszServer /*=NULL*/) { HRESULT hr = S_OK; DBGENTRY(CNmLDAP::LogonAsync); TRACE_OUT(("CNmLDAP::LogonAsync:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); // Idle // Normal logon //Binding // Op_Logoff // Change it to logon refresh // Bound // ldap_umbind-> Logon normal // LoggingOff // Change to Op_Refresh_Logoff // Uninitialized // return error // AddingUser // do nothing // UserAdded // do nothing // SettingAppInfo // do nothing // ModifyingAttrs // do nothing // LoggedIn // do nothing // Op_Refresh_Logoff // Do nothing // Op_Modifying_InCallAttr // Do Nothing // Op_Logon // Do Nothing if( Uninitialized != m_State) { if(Bound == m_State) { _AbandonAllAndSetToIdle(); } if(Idle == m_State) { if(pcszServer) { m_strServer = pcszServer; m_iPort = DEFAULT_LDAP_PORT; } else { if(!IsLoggedOn()) { _GetUserSettingsFromRegistryAndGlobals(); } } m_strCurrentServer = m_strServer; g_pCCallto->SetIlsServerName( m_strCurrentServer ); TCHAR* szDN = new TCHAR[ lstrlen( LOGIN_USER_FILTER ) + lstrlen(m_strEmailName) + 1 ]; if( szDN ) { wsprintf(szDN, LOGIN_USER_FILTER, m_strEmailName); m_strCurrentDN = szDN; delete [] szDN; m_CurrentOp = Op_Logon; hr = _BindAsync(); if( SUCCEEDED(hr) ) { _OnLoggingOn(); } } else { hr = E_OUTOFMEMORY; } } else if((Op_Logoff == m_CurrentOp) || (LoggingOff == m_State)) { m_CurrentOp = Op_Refresh_Logoff; } } else { hr = E_FAIL; } TRACE_OUT(("CNmLDAP::LogonAsync:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::LogonAsync,hr); return hr; } HRESULT CNmLDAP::_BindAsync() { DBGENTRY(CNmLDAP::_BindAsync); TRACE_OUT(("CNmLDAP::_BindAsync:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); ASSERT(( LoggedIn == m_State ) || ( Idle == m_State )); ASSERT( m_CurrentOp != Op_NoOp ); ASSERT(m_hBindThread == NULL); HRESULT hr = S_OK; // Set our state to Binding m_State = Binding; SetLoggedOn( IsLoggedOn() ); // Start the bind in another thread DWORD dwBindThreadID = 0; m_hBindThread = CreateThread(NULL, 0, _sAsyncBindThreadFn, this, 0, &dwBindThreadID); if(NULL == m_hBindThread) { ERROR_OUT(("CreateThread failed")); hr = HRESULT_FROM_WIN32(GetLastError()); WLDAP::ldap_unbind(m_pLdap); m_pLdap = NULL; m_CurrentOp = Op_NoOp; m_State = Idle; } SetLoggedOn( IsLoggedOn() ); TRACE_OUT(("CNmLDAP::_BindAsync:exit(0x%08X): m_State:%d: m_currentOp:%d", hr, m_State, m_CurrentOp)); DBGEXIT_HR(CNmLDAP::_BindAsync,hr); return hr; } /*static*/ DWORD CNmLDAP::_sAsyncBindThreadFn(LPVOID lpParameter) { DBGENTRY(CNmLDAP::_sAsyncBindThreadFn); ASSERT(lpParameter); CNmLDAP* pThis = reinterpret_cast(lpParameter); ASSERT(pThis); ULONG ulRet = LDAP_OPERATIONS_ERROR; ULONG ulErr = ERROR_INVALID_HANDLE; if( pThis ) { WSADATA wsaData; if( WSAStartup( 0x0101, &wsaData ) == 0 ) { if( wsaData.wVersion > 1 ) { pThis->m_pLdap = WLDAP::ldap_init( const_cast((LPCTSTR)pThis->m_strCurrentServer), pThis->m_iPort ); if( pThis->m_pLdap ) { ::SetLastError(NO_ERROR); ulRet = WLDAP::ldap_bind_s(pThis->m_pLdap, const_cast(g_szEmpty), const_cast(g_szEmpty), LDAP_AUTH_SIMPLE ); if( ulRet == LDAP_SUCCESS ) { ulErr = ::GetLastError(); ::SendMessage(pThis->m_hWndHidden, WM_USER_BIND_COMPLETE, ulRet, ulErr); } else if( pThis->m_iPort == DEFAULT_LDAP_PORT ) { WLDAP::ldap_unbind( pThis->m_pLdap ); pThis->m_pLdap = WLDAP::ldap_init( const_cast((LPCTSTR)pThis->m_strCurrentServer), ALTERNATE_LDAP_PORT); // Automatically retry with alternate port... if( pThis->m_pLdap != NULL ) { ::SetLastError(NO_ERROR); ulRet = WLDAP::ldap_bind_s(pThis->m_pLdap, const_cast(g_szEmpty), const_cast(g_szEmpty), LDAP_AUTH_SIMPLE ); if( ulRet == LDAP_SUCCESS ) { pThis->m_iPort = ALTERNATE_LDAP_PORT; } ulErr = ::GetLastError(); ::SendMessage(pThis->m_hWndHidden, WM_USER_BIND_COMPLETE, ulRet, ulErr); } else { TRACE_OUT(("ldap_init failed")); pThis->m_State = Idle; pThis->m_CurrentOp = Op_NoOp; } } } else { TRACE_OUT(("ldap_init failed")); pThis->m_State = Idle; pThis->m_CurrentOp = Op_NoOp; } } WSACleanup(); } } DBGEXIT(CNmLDAP::_sAsyncBindThreadFn); return ulRet; } LRESULT CNmLDAP::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lr = TRUE; DBGENTRY(CNmLDAP::WndProc); switch( uMsg ) { case WM_USER_BIND_COMPLETE: _OnUserBindComplete((INT)wParam, (DWORD)lParam); break; case WM_TIMER: lr = FALSE; // This means that we handle the message _OnTimer(wParam); break; case WM_NEED_RELOGON: OnReLogon(); break; default: lr = DefWindowProc(hwnd, uMsg, wParam, lParam); break; } DBGEXIT_ULONG(CNmLDAP::WndProc,lr); return lr; } void CNmLDAP::_OnTimer(UINT_PTR TimerID) { DBGENTRY(CNmLDAP::_OnTimer); TRACE_OUT(("CNmLDAP::_OnTimer:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); // We check the timer ID values first because we may have killed the timer, // and the message couldv be simply vestigal ( that means left over from an earlier time)... if(m_LogoffTimer && (WaitForLogoffTimer == TimerID)) { // We are waiting in the distructor for an async operation to complete // and we have waited too long... kill the timer and set the m_hEventWaitForLogoffDone event ::KillTimer(m_hWndHidden, m_LogoffTimer); m_LogoffTimer = 0; if(m_hEventWaitForLogoffDone) { SetEvent(m_hEventWaitForLogoffDone); } } else if( (INVALID_MSG_ID != m_uMsgID) && m_ResultPollTimer && (PollForResultTimer == TimerID )) { ASSERT(m_pLdap); ::KillTimer( m_hWndHidden, m_ResultPollTimer ); m_ResultPollTimer = 0; LDAPMessage *pMsg = NULL; LDAP_TIMEVAL TimeoutVal = { 0, 0 }; INT res = WLDAP::ldap_result( m_pLdap, m_uMsgID, LDAP_MSG_ALL, &TimeoutVal, &pMsg ); if( 0 == res ) { // ldap_result timedout m_ResultPollTimer = ::SetTimer( m_hWndHidden, PollForResultTimer, RESULT_POLL_INTERVAL, NULL ); if( !m_ResultPollTimer ) { ERROR_OUT(("SetTimer failed!")); _AbandonAllAndRecoverState(); } } else if( LDAP_RESULT_ERROR == res ) { ERROR_OUT(("ldap_result failed")); _AbandonAllAndRecoverState(); } else { // We got the result, so reset this m_uMsgID = INVALID_MSG_ID; if( AddingUser == m_State ) { _OnAddingUserResult(pMsg->lm_returncode); WLDAP::ldap_msgfree(pMsg); } else if( ( SettingAppInfo == m_State ) || ( ModifyingAttrs == m_State ) ) { _OnSettingAppInfoOrModifyingAttrsResult(pMsg->lm_returncode); WLDAP::ldap_msgfree(pMsg); } else if( LoggingOff == m_State ) { _OnLoggingOffResult(pMsg->lm_returncode); WLDAP::ldap_msgfree(pMsg); } } } TRACE_OUT(("CNmLDAP::_OnTimer:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnTimer); } void CNmLDAP::_OnUserBindComplete(INT LdapResult, DWORD LastError ) { DBGENTRY(CNmLDAP::_OnUserBindComplete); TRACE_OUT(("CNmLDAP::_OnUserBindComplete:enter(%d): m_State:%d: m_currentOp:%d", LdapResult, m_State, m_CurrentOp)); ASSERT(m_hBindThread); ASSERT(Binding == m_State); CloseHandle(m_hBindThread); m_hBindThread = NULL; if(LDAP_SUCCESS == LdapResult) { m_State = Bound; SetLoggedOn( IsLoggedOn() ); if( m_bRefreshAfterBindCompletes ) { // If a refresh is attempted while // the bind operation is in progress // we just wait for the bind to complete // then we well do the refresh m_bRefreshAfterBindCompletes = false; _RefreshServer(); } else if( m_bLogoffAfterBindCompletes ) { m_bLogoffAfterBindCompletes = false; _DeleteUser(); } else { if( Op_Logon == m_CurrentOp ) { m_CurrentOp = Op_NoOp; _AddUser(); } else if( Op_Logoff == m_CurrentOp ) { m_CurrentOp = Op_NoOp; _DeleteUser(); } else if( Op_Refresh_Logoff == m_CurrentOp ) { _DeleteUser(); } else if( Op_Modifying_InCallAttr == m_CurrentOp ) { m_CurrentOp = Op_NoOp; _ModifyInCallAttr(); } } } else { eCurrentOp OldOp = m_CurrentOp; _AbandonAllAndRecoverState(OldOp); if( ( Op_Refresh_Logoff == OldOp ) && ( lstrcmpi( m_strCurrentServer, m_strServer ) ) ) { // If the server names have changed... // Suppose that the server you are logged on to // goes down and then you change the server that you want // to be logged on to ( before you have any indication that // the server has gone down... You want to log off the old server // But the server is not available... We are basically going to // ignore the fact that there was a problem logging off // from the server, and we are going to simply log on to the new server _OnLoggedOff(); LogonAsync(); } else if( Op_Logoff == OldOp ) { // We don't have to put up a message box... _OnLoggedOff(); } else { if( (LDAP_SERVER_DOWN == LdapResult) || (LDAP_TIMEOUT == LdapResult) ) { if (Op_Modifying_InCallAttr == OldOp) { ERROR_OUT(("ldap_bind for InCallAttr returns server-down or timeout")); } else { ::PostConfMsgBox(IDS_ULSLOGON_BADSERVER); _OnLoggedOff(); } } else { ERROR_OUT(("ldap_bind returned error 0x%08x LdapResult, GetLastError == 0x%08x", LdapResult, LastError)); } } } TRACE_OUT(("CNmLDAP::_OnUserBindComplete:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnUserBindComplete); } void CNmLDAP::_OnLoggingOffResult(int Result) { DBGENTRY(CNmLDAP::_OnLoggingOffResult); TRACE_OUT(("CNmLDAP::_OnLoggingOffResult:enter(%d): m_State:%d: m_currentOp:%d", Result, m_State, m_CurrentOp)); if( (LDAP_SUCCESS == Result) || (LDAP_NO_SUCH_OBJECT == Result) || (LDAP_SERVER_DOWN == Result)) { // We are waiting in the destructor for the ldap logof operations to complete // Now that it has, we hawe to signal the destructor unblocks if(m_hEventWaitForLogoffDone) { SetEvent(m_hEventWaitForLogoffDone); } _OnLoggedOff(); } else { ERROR_OUT(("Ldap Error 0x%08x", Result)); } WLDAP::ldap_unbind(m_pLdap); m_State = Idle; m_pLdap = NULL; SetLoggedOn( IsLoggedOn() ); if( Op_Refresh_Logoff == m_CurrentOp ) { m_CurrentOp = Op_NoOp; LogonAsync(); } TRACE_OUT(("CNmLDAP::_OnLoggingOffResult:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnLoggingOffResult); } void CNmLDAP::_OnSettingAppInfoOrModifyingAttrsResult(int Result) { DBGENTRY(CNmLDAP::_OnSettingAppInfoOrModifyingAttrsResult); TRACE_OUT(("CNmLDAP::_OnSettingAppInfoOrModifyingAttrsResult:enter(%d): m_State:%d: m_currentOp:%d", Result, m_State, m_CurrentOp)); if( LDAP_SUCCESS == Result) { // start the keep alive thread if (NULL != m_pKeepAlive) { m_pKeepAlive->Start(); } if(SettingAppInfo == m_State) { m_State = LoggedIn; SetLoggedOn( IsLoggedOn() ); _OnLoggedOn(); } else { m_State = LoggedIn; SetLoggedOn( IsLoggedOn() ); } if( m_bSendInCallAttrWhenDone ) { m_bSendInCallAttrWhenDone = false; _ModifyInCallAttr(); } else { WLDAP::ldap_unbind(m_pLdap); if(m_ResultPollTimer) { ::KillTimer(m_hWndHidden,m_ResultPollTimer); m_ResultPollTimer = 0; } m_uMsgID = INVALID_MSG_ID; m_pLdap = NULL; m_State = LoggedIn; SetLoggedOn( IsLoggedOn() ); } } else { ERROR_OUT( ("Ldap Error 0x%08x, DN=%s", Result, m_strCurrentDN) ); if (ModifyingAttrs == m_State && NULL != m_pKeepAlive) { _AbandonAllAndRecoverState(); if(LDAP_NO_SUCH_OBJECT == Result) { _OnLoggedOff(); LogonAsync(); } } else { _AbandonAllAndSetToIdle(); _OnLoggedOff(); if(LDAP_NO_SUCH_OBJECT == Result) { LogonAsync(); } } } TRACE_OUT(("CNmLDAP::_OnSettingAppInfoOrModifyingAttrsResult:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnSettingAppInfoOrModifyingAttrsResult); } void CNmLDAP::_OnAddingUserResult( int Result) { DBGENTRY(CNmLDAP::_OnAddingUserResult); TRACE_OUT(("CNmLDAP::_OnAddingUserResult:enter(%d): m_State:%d: m_currentOp:%d", Result, m_State, m_CurrentOp)); if( LDAP_SUCCESS == Result ) { m_State = UserAdded; SetLoggedOn( IsLoggedOn() ); _SetAppInfo(); } else { UINT uStringID; switch( Result ) { case LDAP_ALREADY_EXISTS: uStringID = IDS_ULSLOGON_DUPLICATE; break; case LDAP_NAMING_VIOLATION: uStringID = IDS_ULSLOGON_WORD_FILTER; break; case LDAP_UNWILLING_TO_PERFORM: uStringID = IDS_ILLEGALEMAILNAME; break; case LDAP_UNDEFINED_TYPE: case LDAP_SERVER_DOWN: // W2K server returns this under heavy load situations... uStringID = IDS_ULSLOGON_ERROR; break; default: uStringID = IDS_ULSLOGON_ERROR; ERROR_OUT( ("Ldap Error 0x%08x, DN=%s", Result, m_strCurrentDN) ); break; } ::PostConfMsgBox( uStringID ); _OnLoggedOff(); WLDAP::ldap_unbind(m_pLdap); m_pLdap = NULL; m_State = Idle; SetLoggedOn( IsLoggedOn() ); } TRACE_OUT(("CNmLDAP::_OnAddingUserResult:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnAddingUserResult); } /*static*/ LRESULT CNmLDAP::_sWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lr = 0; if( WM_NCCREATE != uMsg ) { CNmLDAP* pNmLdap = reinterpret_cast( ::GetWindowLongPtr( hwnd, GWLP_USERDATA ) ); if( pNmLdap ) { lr = pNmLdap->WndProc( hwnd, uMsg, wParam, lParam ); } } else { lr = TRUE; // This means to continue creating the window CREATESTRUCT* pCreateStruct = reinterpret_cast(lParam); if( pCreateStruct ) { ::SetWindowLongPtr( hwnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); } } return lr; } void CNmLDAP::_SetAppInfo() { DBGENTRY(CNmLDAP::_SetInfo); TRACE_OUT(("CNmLDAP::_SetInfo:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); ASSERT(NULL != m_pLdap); ASSERT(UserAdded == m_State); ASSERT(INVALID_MSG_ID == m_uMsgID); // These are the app attribute vals DECLARE_LDAP_MOD(LDAP_MOD_ADD, smodop, LDAP_MODOP_ADDAPP); DECLARE_LDAP_MOD(LDAP_MOD_ADD, sappid, _T("ms-netmeeting")); DECLARE_LDAP_MOD(LDAP_MOD_ADD, smimetype, _T("text/iuls")); DECLARE_LDAP_MOD(LDAP_MOD_ADD, sappguid, _T("008aff194794cf118796444553540000")); DECLARE_LDAP_MOD2(LDAP_MOD_ADD, sprotid, _T("t120"), _T("h323")); DECLARE_LDAP_MOD2(LDAP_MOD_ADD, sprotmimetype, _T("text/t120"), _T("text/h323")); DECLARE_LDAP_MOD2(LDAP_MOD_ADD, sport, _T("1503"), _T("1720")); // This is the app mod array LDAPMod *apModApp[] = { LDAP_MOD_ENTRY(smodop), LDAP_MOD_ENTRY(sappid), LDAP_MOD_ENTRY(smimetype), LDAP_MOD_ENTRY(sappguid), LDAP_MOD_ENTRY(sprotid), LDAP_MOD_ENTRY(sprotmimetype), LDAP_MOD_ENTRY(sport), NULL }; m_uMsgID = WLDAP::ldap_modify( m_pLdap, const_cast((LPCTSTR)m_strCurrentDN), apModApp ); if( INVALID_MSG_ID != m_uMsgID ) { m_ResultPollTimer = ::SetTimer( m_hWndHidden, PollForResultTimer, RESULT_POLL_INTERVAL, NULL ); ASSERT(m_ResultPollTimer); m_State = SettingAppInfo; } else { DWORD dwErr; ERROR_OUT(("ldap_modify returned error 0x%08x", (WLDAP::ldap_get_option(m_pLdap, LDAP_OPT_ERROR_NUMBER, &dwErr), dwErr))); WLDAP::ldap_unbind(m_pLdap); m_pLdap = NULL; m_State = Idle; } SetLoggedOn( IsLoggedOn() ); TRACE_OUT(("CNmLDAP::_SetInfo:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_SetInfo); } void CNmLDAP::_ModifyInCallAttr() { TRACE_OUT(("CNmLDAP::_ModifyInCallAttr:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DECLARE_LDAP_MOD(LDAP_MOD_REPLACE, smodop, LDAP_MODOP_MODIFYUSER); DECLARE_LDAP_MOD(LDAP_MOD_REPLACE, ilsA26214430, m_bInCall ? LDAP_ATTR_TRUE : LDAP_ATTR_FALSE); LDAPMod *apMod[] = { LDAP_MOD_ENTRY(smodop), LDAP_MOD_ENTRY(ilsA26214430), NULL }; TRACE_OUT(("About to modify InCallAttrs for this person: %s", m_strCurrentDN)); m_uMsgID = WLDAP::ldap_modify( m_pLdap, const_cast((LPCTSTR)m_strCurrentDN), apMod ); if( INVALID_MSG_ID != m_uMsgID ) { m_ResultPollTimer = ::SetTimer( m_hWndHidden, PollForResultTimer, RESULT_POLL_INTERVAL, NULL ); ASSERT(m_ResultPollTimer); m_State = ModifyingAttrs; } else { DWORD dwErr; ERROR_OUT(("ldap_modify returned error 0x%08x", (WLDAP::ldap_get_option(m_pLdap, LDAP_OPT_ERROR_NUMBER, &dwErr), dwErr))); WLDAP::ldap_unbind(m_pLdap); m_pLdap = NULL; m_State = (NULL != m_pKeepAlive) ? LoggedIn : Idle; // restore the state } SetLoggedOn( IsLoggedOn() ); TRACE_OUT(("CNmLDAP::_ModifyInCallAttr:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); } void CNmLDAP::_DeleteUser() { DBGENTRY(CNmLDAP::_DeleteUser); TRACE_OUT(("CNmLDAP::_DeleteUser:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); // end the keep alive thread if (NULL != m_pKeepAlive) { m_pKeepAlive->End(); m_pKeepAlive = NULL; } ASSERT(NULL != m_pLdap); ASSERT(Bound == m_State); ASSERT(INVALID_MSG_ID == m_uMsgID); m_uMsgID = WLDAP::ldap_delete(m_pLdap, const_cast((LPCTSTR)(m_strCurrentDN))); if( INVALID_MSG_ID != m_uMsgID ) { m_State = LoggingOff; m_ResultPollTimer = ::SetTimer( m_hWndHidden, PollForResultTimer, RESULT_POLL_INTERVAL, NULL ); ASSERT(m_ResultPollTimer); } else { WLDAP::ldap_unbind(m_pLdap); m_pLdap = NULL; m_State = Idle; } SetLoggedOn( IsLoggedOn() ); TRACE_OUT(("CNmLDAP::_DeleteUser:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_DeleteUser); } void CNmLDAP::_AddUser() { DBGENTRY(CNmLDAP::_AddUser); TRACE_OUT(("CNmLDAP::_AddUser:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); ASSERT(NULL != m_pLdap); ASSERT(Bound == m_State); ASSERT(INVALID_MSG_ID == m_uMsgID); // These are the user attribute values DECLARE_LDAP_MOD(LDAP_MOD_ADD, cn, const_cast((LPCTSTR)m_strEmailName)); DECLARE_LDAP_MOD(LDAP_MOD_ADD, givenname, const_cast((LPCTSTR)m_strGivenName)); DECLARE_LDAP_MOD(LDAP_MOD_ADD, surname, const_cast((LPCTSTR)m_strSurName)); DECLARE_LDAP_MOD(LDAP_MOD_ADD, rfc822mailbox, const_cast((LPCTSTR)m_strEmailName)); DECLARE_LDAP_MOD(LDAP_MOD_ADD, location, const_cast((LPCTSTR)m_strLocation)); DECLARE_LDAP_MOD(LDAP_MOD_ADD, comment, const_cast((LPCTSTR)m_strComment)); DECLARE_LDAP_MOD(LDAP_MOD_ADD, sflags, m_bVisible ? LDAP_ATTR_TRUE : LDAP_ATTR_FALSE ); DECLARE_LDAP_MOD(LDAP_MOD_ADD, ilsA26214430, m_bInCall ? LDAP_ATTR_TRUE : LDAP_ATTR_FALSE ); DECLARE_LDAP_MOD(LDAP_MOD_ADD, ilsA32833566, m_bAudioHardware ? LDAP_ATTR_TRUE : LDAP_ATTR_FALSE ); DECLARE_LDAP_MOD(LDAP_MOD_ADD, ilsA32964638, m_bVideoHardware ? LDAP_ATTR_TRUE : LDAP_ATTR_FALSE ); DECLARE_LDAP_MOD(LDAP_MOD_ADD, ilsA39321630, LDAP_ATTR_BUSINESS ); DECLARE_LDAP_MOD(LDAP_MOD_ADD, ssecurity, const_cast((LPCTSTR)m_strSecurityToken)); DECLARE_LDAP_MOD(LDAP_MOD_ADD, c, LDAP_ATTR_DUMMY_COUNTRYNAME); // We have to get IP address dynamically TCHAR szIpAddr[MAX_PATH]; szIpAddr[0] = _T('\0'); DWORD dwLocalIPAddress = INADDR_NONE; _GetIpAddressOfLdapSession( szIpAddr, CCHMAX(szIpAddr), &dwLocalIPAddress ); DECLARE_LDAP_MOD(LDAP_MOD_ADD, sipaddress, szIpAddr); // We store the version info on the server TCHAR szVer[MAX_PATH]; wsprintf(szVer,"%lu",VER_PRODUCTVERSION_DW); DECLARE_LDAP_MOD(LDAP_MOD_ADD, ilsA26279966, szVer); // This is the user mod array LDAPMod *apModUser[] = { LDAP_MOD_ENTRY(cn), LDAP_MOD_ENTRY(givenname), LDAP_MOD_ENTRY(surname), LDAP_MOD_ENTRY(rfc822mailbox), LDAP_MOD_ENTRY(location), LDAP_MOD_ENTRY(comment), LDAP_MOD_ENTRY(c), LDAP_MOD_ENTRY(sipaddress), LDAP_MOD_ENTRY(sflags), LDAP_MOD_ENTRY(ssecurity), LDAP_MOD_ENTRY(ilsA26214430), LDAP_MOD_ENTRY(ilsA26279966), LDAP_MOD_ENTRY(ilsA32833566), LDAP_MOD_ENTRY(ilsA32964638), LDAP_MOD_ENTRY(ilsA39321630), NULL }; TCHAR* szDN = new TCHAR[ lstrlen(LOGIN_USER_FILTER) + lstrlen(m_strEmailName) + 1 ]; if( szDN ) { wsprintf(szDN, LOGIN_USER_FILTER, m_strEmailName); m_strCurrentDN = szDN; delete [] szDN; } TCHAR* szRefresh = new TCHAR[ lstrlen(LDAP_REFRESH_FILTER) + lstrlen(m_strEmailName) + 1 ]; if( szRefresh ) { wsprintf(szRefresh, LDAP_REFRESH_FILTER, m_strEmailName); } m_uMsgID = WLDAP::ldap_add( m_pLdap, const_cast((LPCTSTR)m_strCurrentDN), apModUser ); if( INVALID_MSG_ID != m_uMsgID ) { m_State = AddingUser; m_ResultPollTimer = ::SetTimer( m_hWndHidden, PollForResultTimer, RESULT_POLL_INTERVAL, NULL ); ASSERT(m_ResultPollTimer); // create the keep alive object if (NULL != m_pKeepAlive) { m_pKeepAlive->End(); m_pKeepAlive = NULL; } BOOL fRet = FALSE; m_pKeepAlive = new CKeepAlive(&fRet, m_hWndHidden, dwLocalIPAddress, m_strCurrentServer, m_iPort, szRefresh); ASSERT(NULL != m_pKeepAlive && fRet); } else { WLDAP::ldap_unbind(m_pLdap); m_pLdap = NULL; m_State = Idle; } delete [] szRefresh; SetLoggedOn( IsLoggedOn() ); TRACE_OUT(("CNmLDAP::_AddUser:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_AddUser); } void CNmLDAP::_OnLoggedOn() { DBGENTRY(CNmLDAP::_OnLoggedOn); TRACE_OUT(("CNmLDAP::_OnLoggedOn:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); ::UpdateUI(CRUI_STATUSBAR | CRUI_CALLANIM, TRUE); TRACE_OUT(("CNmLDAP::_OnLoggedOn:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnLoggedOn); } void CNmLDAP::_OnLoggedOff() { DBGENTRY(CNmLDAP::_OnLoggedOff); TRACE_OUT(("CNmLDAP::_OnLoggedOff:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); ::UpdateUI(CRUI_STATUSBAR | CRUI_CALLANIM, TRUE); TRACE_OUT(("CNmLDAP::_OnLoggedOff:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnLoggedOff); } void CNmLDAP::_OnLoggingOn() { DBGENTRY(CNmLDAP::_OnLoggingOn); TRACE_OUT(("CNmLDAP::_OnLoggingOn:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); ::UpdateUI(CRUI_STATUSBAR | CRUI_CALLANIM, TRUE); TRACE_OUT(("CNmLDAP::_OnLoggingOn:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnLoggingOn); } void CNmLDAP::_OnLoggingOff() { DBGENTRY(CNmLDAP::_OnLoggingOff); TRACE_OUT(("CNmLDAP::_OnLoggingOff:enter: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); ::UpdateUI(CRUI_STATUSBAR | CRUI_CALLANIM, TRUE); TRACE_OUT(("CNmLDAP::_OnLoggingOff:exit: m_State:%d: m_currentOp:%d", m_State, m_CurrentOp)); DBGEXIT(CNmLDAP::_OnLoggingOff); } void CNmLDAP::_GetIpAddressOfLdapSession( LPTSTR szIpAddr, int cchMax, DWORD *pdwIPAddr ) { DBGENTRY(CNmLDAP::_GetIpAddressOfLdapSession); if (NULL != szIpAddr) { szIpAddr[0] = _T('\0'); } SOCKET s = INVALID_SOCKET; if( LDAP_SUCCESS == WLDAP::ldap_get_option(m_pLdap, LDAP_OPT_DESC, &s )) { SOCKADDR_IN addr; int NameLen = sizeof(addr); if(0 == getsockname(s, reinterpret_cast(&addr), &NameLen)) { if (NULL != szIpAddr) { wsprintf(szIpAddr, "%u", addr.sin_addr.s_addr); } if (NULL != pdwIPAddr) { *pdwIPAddr = addr.sin_addr.s_addr; } } } DBGEXIT(CNmLDAP::_GetIpAddressOfLdapSession); } void InitNmLdapAndLogon() { ASSERT( NULL == g_pLDAP ); g_pLDAP = new CNmLDAP; if(g_pLDAP) { // Initialize the LDAP object... if( SUCCEEDED(g_pLDAP->Initialize( _Module.GetModuleInstance()))) { g_pLDAP->LogonAsync(); } else { delete g_pLDAP; g_pLDAP = NULL; } } else { ERROR_OUT(("new CNmLDAP returned NULL")); } } ///////////////////////////////////////////////////////// // // CKeepAlive // void CALLBACK KeepAliveTimerProc(HWND, UINT, UINT_PTR, DWORD) { // doing nothing at all } DWORD KeepAliveThreadProc(LPVOID pParam) { // make sure we have a valid socket WSADATA wsaData; int iRet = ::WSAStartup(0x0101, &wsaData); ASSERT(! iRet); if (! iRet) { CKeepAlive *pKeepAlive = (CKeepAlive *) pParam; ASSERT(NULL != pKeepAlive); // translate server name into ip address if (pKeepAlive->SetServerIPAddress()) { // start the timer UINT nKeepAliveInterval = INITIAL_REFRESH_INTERVAL_MINUTES; UINT_PTR nTimerID = ::SetTimer(NULL, 0, nKeepAliveInterval * 60 * 1000, KeepAliveTimerProc); ASSERT(nTimerID); // watch for the timer message MSG msg, msg2; while (::GetMessage(&msg, NULL, 0, 0)) { msg2 = msg; // keep a copy of this message // dispatch messages ::DispatchMessage(&msg); // intercept the WM_TIMER message thru msg2 if (WM_TIMER == msg2.message && nTimerID == msg2.wParam) { BOOL fRetry = TRUE; // kill the timer to avoid timer overrun ::KillTimer(NULL, nTimerID); nTimerID = 0; // ping the server in case of dialup networking if (pKeepAlive->Ping()) { // connect to the server LDAP *ld = WLDAP::ldap_open(pKeepAlive->GetServerName(), pKeepAlive->GetServerPortNumber()); if (NULL != ld) { if (pKeepAlive->Bind(ld)) { if (pKeepAlive->KeepAlive(ld, &nKeepAliveInterval)) { // successfully send a keep alive fRetry = FALSE; DWORD dwLocalIPAddress = pKeepAlive->GetLocalIPAddress(ld); if (INADDR_NONE != dwLocalIPAddress) { if (pKeepAlive->GetLocalIPAddress() != dwLocalIPAddress) { pKeepAlive->SetLocalIPAddress(dwLocalIPAddress); pKeepAlive->UpdateIPAddressOnServer(); } } } } WLDAP::ldap_unbind(ld); } } // start the new timer based on the new internal nTimerID = ::SetTimer(NULL, 0, fRetry ? (15 * 1000) : (nKeepAliveInterval * 60 * 1000), KeepAliveTimerProc); ASSERT(nTimerID); } // if wm timer } // while get message if (nTimerID) { ::KillTimer(NULL, nTimerID); } } delete pKeepAlive; ::WSACleanup(); } return 0; } CKeepAlive::CKeepAlive ( BOOL *pfRet, HWND hwndMainThread, DWORD dwLocalIPAddress, const TCHAR * const pcszServerName, UINT nPort, LPTSTR pszKeepAliveFilter ) : m_hwndMainThread(hwndMainThread), m_dwLocalIPAddress(dwLocalIPAddress), m_dwServerIPAddress(INADDR_NONE), m_pszServerName(NULL), m_nPort(nPort), m_pszKeepAliveFilter(NULL), m_hThread(NULL), m_dwThreadID(0), m_fAborted(FALSE) { *pfRet = FALSE; // assume failure // sanity check ASSERT(NULL != hwndMainThread); ASSERT(INADDR_NONE != dwLocalIPAddress && 0 != dwLocalIPAddress); ASSERT(nPort); // create the server name ASSERT(NULL != pcszServerName); ULONG nStrLen = ::lstrlen(pcszServerName) + 1; m_pszServerName = new TCHAR[nStrLen]; if (NULL != m_pszServerName) { ::CopyMemory(m_pszServerName, pcszServerName, nStrLen * sizeof(TCHAR)); TCHAR * const pszPort = StrChr( m_pszServerName, ':' ); if( pszPort != NULL ) { // Truncate the server name here for dns lookup.... // and this port number overrides the nPort parameter... *pszPort = '\0'; HRESULT hrResult = DecimalStringToUINT( pszPort + 1, m_nPort ); ASSERT( hrResult == S_OK ); if( hrResult != S_OK ) { m_nPort = DEFAULT_LDAP_PORT; } } // create the fresh filter ASSERT(NULL != pszKeepAliveFilter); nStrLen = ::lstrlen(pszKeepAliveFilter) + 1; m_pszKeepAliveFilter = new TCHAR[nStrLen]; if (NULL != m_pszKeepAliveFilter) { ::CopyMemory(m_pszKeepAliveFilter, pszKeepAliveFilter, nStrLen * sizeof(TCHAR)); *pfRet = TRUE; } } } CKeepAlive::~CKeepAlive(void) { delete m_pszServerName; delete m_pszKeepAliveFilter; } BOOL CKeepAlive::Start(void) { if (! m_dwThreadID) { ASSERT(NULL == m_hThread); // create the worker thread m_hThread = ::CreateThread(NULL, 0, KeepAliveThreadProc, this, 0, &m_dwThreadID); } ASSERT(NULL != m_hThread); return (NULL != m_hThread); } BOOL CKeepAlive::End(BOOL fSync) { DWORD dwRet = WAIT_OBJECT_0; // cache thread handle and ID HANDLE hThread = m_hThread; DWORD dwThreadID = m_dwThreadID; // abort any pending operation m_fAborted = TRUE; // notify the worker thread to go away if (m_dwThreadID) { ASSERT(NULL != m_hThread); m_dwThreadID = 0; ::PostThreadMessage(dwThreadID, WM_QUIT, 0, 0); } // wait for the worker thread exit for 5 seconds if (NULL != hThread) { // need more work to unblock it if (fSync) { dwRet = ::WaitForSingleObject(hThread, 5000); // 5 second timeout ASSERT(WAIT_TIMEOUT != dwRet); } ::CloseHandle(hThread); } return (WAIT_TIMEOUT != dwRet); } BOOL CKeepAlive::SetServerIPAddress(void) { ASSERT(NULL != m_pszServerName); // check to see if the server name is a dotted IP address string m_dwServerIPAddress = ::inet_addr(m_pszServerName); if (INADDR_NONE == m_dwServerIPAddress) { // it is not a dotted string, it must be a name. // get the host entry by name PHOSTENT phe = ::gethostbyname(m_pszServerName); if (phe != NULL) { // get info from the host entry m_dwServerIPAddress = *(DWORD *) phe->h_addr; } } ASSERT(INADDR_NONE != m_dwServerIPAddress); return (INADDR_NONE != m_dwServerIPAddress); } BOOL CKeepAlive::Ping(void) { BOOL fRet = TRUE; // assume success if (NULL != g_pPing) { if (g_pPing->IsAutodialEnabled()) { ASSERT(INADDR_NONE != m_dwServerIPAddress); HRESULT hr = g_pPing->Ping(m_dwServerIPAddress, PING_TIMEOUT_INTERVAL, PING_RETRIES); fRet = (S_OK == hr); } } return fRet; } BOOL CKeepAlive::Bind(LDAP *ld) { ASSERT(NULL != ld); if (! m_fAborted) { // anonymous bind ULONG nMsgID = WLDAP::ldap_bind(ld, TEXT(""), TEXT(""), LDAP_AUTH_SIMPLE); if (INVALID_MSG_ID != nMsgID) { // poll the result every quarter second const ULONG c_nTimeoutInQuarterSecond = 4 * LDAP_TIMEOUT_IN_SECONDS; for (ULONG i = 0; (i < c_nTimeoutInQuarterSecond) && (! m_fAborted); i++) { // no timeout, if no result, return immediately LDAP_TIMEVAL TimeVal; TimeVal.tv_usec = 0; TimeVal.tv_sec = 0; // check the result LDAPMessage *pMsg = NULL; ULONG nResultType = WLDAP::ldap_result(ld, nMsgID, LDAP_MSG_ALL, &TimeVal, &pMsg); if (nResultType == LDAP_RES_BIND) { ASSERT(NULL != pMsg); WLDAP::ldap_msgfree(pMsg); return TRUE; } // deal with timeout or error if (LDAP_RESULT_TIMEOUT == nResultType) { if (! m_fAborted) { ::Sleep(250); // sleep for a quarter second continue; } } ASSERT(LDAP_RESULT_ERROR != nResultType); break; } // failure, do the cleanup WLDAP::ldap_abandon(ld, nMsgID); } } return FALSE; } DWORD CKeepAlive::GetLocalIPAddress(LDAP *ld) { SOCKET s = INVALID_SOCKET; if (LDAP_SUCCESS == WLDAP::ldap_get_option(ld, LDAP_OPT_DESC, &s)) { SOCKADDR_IN addr; int NameLen = sizeof(addr); if (0 == ::getsockname(s, (SOCKADDR *) &addr, &NameLen)) { return addr.sin_addr.s_addr; } } return INADDR_NONE; } BOOL CKeepAlive::KeepAlive(LDAP *ld, UINT *pnKeepAliveInterval) { LPTSTR aTTLAttr[] = { STTL_ATTR_NAME, NULL }; UINT nMsgID = WLDAP::ldap_search(ld, LDAP_REFRESH_BASE_DN, LDAP_SCOPE_BASE, m_pszKeepAliveFilter, aTTLAttr, FALSE); if (INVALID_MSG_ID != nMsgID) { // poll the result every quarter second const ULONG c_nTimeoutInQuarterSecond = 4 * LDAP_TIMEOUT_IN_SECONDS; BOOL fError = FALSE; for (ULONG i = 0; (i < c_nTimeoutInQuarterSecond) && (! m_fAborted) && (! fError); i++) { // no timeout, if no result, return immediately LDAP_TIMEVAL TimeVal; TimeVal.tv_usec = 0; TimeVal.tv_sec = 0; // check the result LDAPMessage *pMsg = NULL; ULONG nResultType = WLDAP::ldap_result(ld, nMsgID, LDAP_MSG_ALL, &TimeVal, &pMsg); switch (nResultType) { case LDAP_RESULT_TIMEOUT: if (! m_fAborted) { ::Sleep(250); // sleep for a quarter second } break; case LDAP_RESULT_ERROR: fError = TRUE; break; default: ASSERT(LDAP_RES_SEARCH_ENTRY == nResultType || LDAP_RES_SEARCH_RESULT == nResultType); ASSERT(NULL != pMsg); switch (pMsg->lm_returncode) { case LDAP_SUCCESS: GetNewInterval(ld, pMsg, pnKeepAliveInterval); break; case LDAP_NO_SUCH_OBJECT: ReLogon(); fError = TRUE; break; default: break; } WLDAP::ldap_msgfree(pMsg); return (! fError); } } // failure, do the cleanup WLDAP::ldap_abandon(ld, nMsgID); } return FALSE; } void CKeepAlive::GetNewInterval(LDAP *ld, LDAPMessage *pMsg, UINT *pnKeepAliveInterval) { // get the first entry which should contain the new ttl value LDAPMessage *pEntry = WLDAP::ldap_first_entry(ld, pMsg); if (NULL != pEntry) { // get the first attribute which should be the new ttl value BerElement *pElement = NULL; LPTSTR pszAttrib = WLDAP::ldap_first_attribute(ld, pEntry, &pElement); if (NULL != pszAttrib) { // it should be ttl attribute ASSERT(! lstrcmpi(STTL_ATTR_NAME, pszAttrib)); // get the value LPTSTR *ppszTTL = WLDAP::ldap_get_values(ld, pEntry, pszAttrib); if (NULL != ppszTTL) { if (NULL != ppszTTL[0]) { int iRefresh = ::RtStrToInt(ppszTTL[0]) - REFRESH_TIMEOUT_MARGIN; if (iRefresh > 0) { if (iRefresh < MIN_REFRESH_TIMEOUT_INTERVAL_MINUTES) { iRefresh = MIN_REFRESH_TIMEOUT_INTERVAL_MINUTES; } *pnKeepAliveInterval = (UINT) iRefresh; } } WLDAP::ldap_value_free(ppszTTL); } } } } void CKeepAlive::ReLogon(void) { ::PostMessage(m_hwndMainThread, WM_NEED_RELOGON, 0, 0); } void CKeepAlive::UpdateIPAddressOnServer(void) { ::PostMessage(m_hwndMainThread, WM_NEED_RELOGON, 0, 0); } ///////////////////////////////////////////////////////// // // ResolveUser // typedef struct tagResolveInfo { // given DWORD cchMax; LPTSTR pszIPAddress; // created LPTSTR pszSearchFilter; LDAP *ld; } RESOLVE_INFO; void FreeResolveInfo(RESOLVE_INFO *pInfo) { if (NULL != pInfo) { delete pInfo->pszSearchFilter; if (NULL != pInfo->ld) { WLDAP::ldap_unbind(pInfo->ld); } delete pInfo; } } DWORD ResolveUserThreadProc(LPVOID pParam) { RESOLVE_INFO *pInfo = (RESOLVE_INFO *) pParam; ASSERT(NULL != pInfo); // send the search request TCHAR* attrs[] = { IP_ADDRESS_ATTR_NAME, NULL }; ULONG nMsgID = WLDAP::ldap_search(pInfo->ld, TEXT("objectClass=RTPerson"), LDAP_SCOPE_BASE, pInfo->pszSearchFilter, attrs, 0); if (INVALID_MSG_ID != nMsgID) { LDAPMessage *pMsg = NULL; LDAP_TIMEVAL SearchTimeout = { LDAP_TIMEOUT_IN_SECONDS, 0 }; ULONG nResultType = WLDAP::ldap_result(pInfo->ld, nMsgID, LDAP_MSG_ALL, &SearchTimeout, &pMsg); switch (nResultType) { case LDAP_RESULT_TIMEOUT: case LDAP_RESULT_ERROR: WLDAP::ldap_abandon(pInfo->ld, nMsgID); break; default: { ASSERT(LDAP_RES_SEARCH_ENTRY == nResultType || LDAP_RES_SEARCH_RESULT == nResultType); ASSERT(NULL != pMsg); // get the first entry LDAPMessage *pEntry = WLDAP::ldap_first_entry(pInfo->ld, pMsg); if (NULL != pEntry) { BerElement *pElement = NULL; // get the first attribute LPTSTR pszAttrib = WLDAP::ldap_first_attribute(pInfo->ld, pEntry, &pElement); if (NULL != pszAttrib) { ASSERT(! lstrcmpi(IP_ADDRESS_ATTR_NAME, pszAttrib)); // get the value LPTSTR *ppszIPAddress = WLDAP::ldap_get_values(pInfo->ld, pEntry, pszAttrib); if (NULL != ppszIPAddress) { if (NULL != ppszIPAddress[0]) { BYTE temp[sizeof(DWORD)]; *(DWORD *) &temp[0] = ::RtStrToInt(ppszIPAddress[0]); ::wsprintf(pInfo->pszIPAddress, TEXT("%u.%u.%u.%u"), (UINT) temp[0], (UINT) temp[1], (UINT) temp[2], (UINT) temp[3]); } WLDAP::ldap_value_free(ppszIPAddress); } } // if attribute } // if entry WLDAP::ldap_msgfree(pMsg); } break; } // switch } // if msg id return 0; } /*static*/ HRESULT CNmLDAP::ResolveUser( LPCTSTR pcszName, LPCTSTR pcszServer, LPTSTR pszIPAddress, DWORD cchMax, int port ) { HRESULT hr = E_OUTOFMEMORY; RESOLVE_INFO *pInfo = NULL; // clean up the return buffer *pszIPAddress = TEXT('\0'); // make sure the wldap32.dll is loaded if( ms_bLdapDLLLoaded || SUCCEEDED( hr = WLDAP::Init())) { ms_bLdapDLLLoaded = true; // create a resolve info which exchanges info between this thread and a background thread. pInfo = new RESOLVE_INFO; if (NULL != pInfo) { // cleanup ::ZeroMemory(pInfo, sizeof(*pInfo)); // remember return buffer and its size pInfo->pszIPAddress = pszIPAddress; pInfo->cchMax = cchMax; // create search filter ULONG cbFilterSize = ::lstrlen(RESOLVE_USER_SEARCH_FILTER) + ::lstrlen(pcszName) + 2; pInfo->pszSearchFilter = new TCHAR[cbFilterSize]; if (NULL != pInfo->pszSearchFilter) { // construct search filter ::wsprintf(pInfo->pszSearchFilter, RESOLVE_USER_SEARCH_FILTER, pcszName); // create ldap block that is NOT connected to server yet. pInfo->ld = WLDAP::ldap_init(const_cast(pcszServer), port); if( pInfo->ld != NULL ) { ULONG ulResult = WLDAP::ldap_bind_s(pInfo->ld, TEXT(""), TEXT(""), LDAP_AUTH_SIMPLE); if( (ulResult != LDAP_SUCCESS) && (port == DEFAULT_LDAP_PORT) ) { WLDAP::ldap_unbind(pInfo->ld); pInfo->ld = WLDAP::ldap_init(const_cast(pcszServer), ALTERNATE_LDAP_PORT); // Automatically retry with alternate port... if( pInfo->ld != NULL ) { ulResult = WLDAP::ldap_bind_s(pInfo->ld, TEXT(""), TEXT(""), LDAP_AUTH_SIMPLE); if( ulResult != LDAP_SUCCESS ) { WLDAP::ldap_unbind(pInfo->ld); pInfo->ld = NULL; } } } } ASSERT(NULL != pInfo->ld); if (NULL != pInfo->ld) { DWORD dwThreadID = 0; HANDLE hThread = ::CreateThread(NULL, 0, ResolveUserThreadProc, pInfo, 0, &dwThreadID); if (NULL != hThread) { // wait for the thread to exit hr = ::WaitWithMessageLoop(hThread); DWORD dwIPAddr = ::inet_addr(pszIPAddress); hr = (dwIPAddr && INADDR_NONE != dwIPAddr) ? S_OK : E_FAIL; // close thread ::CloseHandle(hThread); } } // if ld } // if search filter } // if new resolve info } // if init ::FreeResolveInfo(pInfo); return hr; }