windows-nt/Source/XPSP1/NT/enduser/netmeeting/ui/conf/ldap.cpp
2020-09-26 16:20:57 +08:00

1415 lines
35 KiB
C++

// File: ldap.cpp
#include "precomp.h"
#include "resource.h"
#include "mapidefs.h"
#include "pfnwldap.h"
#include "ldap.h"
#include "wab.h"
#include "dirutil.h"
#include "dlgcall2.h" // for WM_DISPLAY_MESSAGE
#include "upropdlg.h"
#define CDIRCACHE_IMAGES 3
// static strings
static const TCHAR s_szSearchFormat3[] = TEXT("(&(objectClass=RTPerson)(cn=%s)%s)");
static const TCHAR s_szSearchFormat2[] = TEXT("(&(objectClass=RTPerson)(cn=%s)(sappid=ms-netmeeting)(sprotid=h323)%s)");
static const TCHAR s_szSearchCategory[] = TEXT("(ILSA39321630=%d)");
static LPCTSTR s_pszOrg = TEXT("o=Microsoft");
static const TCHAR s_cszAttribShow[] = TEXT("sFlags"); // "Hide Me" (1=Show, 0=Hidden)
static const TCHAR s_cszAttribEmail[] = TEXT("cn"); // Email address
static const TCHAR s_cszAttribFirstName[] = TEXT("givenName"); // First Name
static const TCHAR s_cszAttribLastName[] = TEXT("surName"); // Last Name
static const TCHAR s_cszAttribLocation[] = TEXT("location"); // Location
static const TCHAR s_cszAttribComment[] = TEXT("comment"); // Comments
static const TCHAR s_cszAttribInACall[] = TEXT("ilsA26214430"); // 400 = in a call
static const TCHAR s_cszAttribVersion[] = TEXT("ilsA26279966"); // 401 = version number
static const TCHAR s_cszAttribAudio[] = TEXT("ilsA32833566"); // 501 = send audio
static const TCHAR s_cszAttribVideo[] = TEXT("ilsA32964638"); // 503 = send video
static const TCHAR s_cszAttribCategory[] = TEXT("ilsA39321630"); // 600 = category
static LPCTSTR s_rgAttrNameAddr[] = {
(LPTSTR) s_cszAttribShow,
(LPTSTR) s_cszAttribEmail,
(LPTSTR) s_cszAttribFirstName,
(LPTSTR) s_cszAttribLastName,
(LPTSTR) s_cszAttribLocation,
(LPTSTR) s_cszAttribInACall,
(LPTSTR) s_cszAttribAudio,
(LPTSTR) s_cszAttribVideo,
(LPTSTR) s_cszAttribComment,
NULL};
static LPCTSTR s_rgAttrAll[] = {
(LPTSTR) s_cszAttribFirstName,
(LPTSTR) s_cszAttribLastName,
(LPTSTR) s_cszAttribComment,
(LPTSTR) s_cszAttribAudio,
(LPTSTR) s_cszAttribVideo,
(LPTSTR) s_cszAttribVersion,
(LPTSTR) s_cszAttribCategory,
NULL};
static const int _rgIdMenu[] = {
IDM_DLGCALL_SPEEDDIAL,
IDM_DLGCALL_WAB,
-1,
IDM_DLGCALL_REFRESH,
IDM_DLGCALL_STOP,
0
};
// Local functions
VOID ConvertVersionInfo(LPTSTR pszVersion, LPTSTR pszCategory);
///////////////////////////////////////////////////////////////////////
CLDAP::CLDAP() :
CALV(0, II_SERVER, _rgIdMenu),
m_pLdap(NULL),
m_ulPort(DEFAULT_LDAP_PORT),
m_msgId(0),
m_hThread(NULL),
m_hWnd( NULL ),
m_hSearchMutex( NULL ),
m_fDirInProgress( FALSE ),
m_cTotalEntries( 0 ),
m_cEntries( 0 ),
m_fHaveRefreshed( FALSE ),
m_dwTickStart( 0 ),
m_fIsCacheable( FALSE ),
m_fNeedsRefresh( FALSE ),
m_bSearchCancelled( false ),
m_uniqueId( 0 )
{
DbgMsg(iZONE_OBJECTS, "CLDAP - Constructed(%08X)", this);
HRESULT hr = WLDAP::Init();
SetAvailable(S_OK == hr);
m_szServer[0] = _T('\0');
m_szAddress[0] = _T('\0');
m_hSearchMutex = CreateMutex( NULL, false, NULL );
RegEntry reUI(UI_KEY, HKEY_CURRENT_USER);
m_fCacheDirectory = (0 != reUI.GetNumber(
REGVAL_CACHE_DIRECTORY, DEFAULT_CACHE_DIRECTORY));
m_cMinutesExpire = reUI.GetNumber(
REGVAL_CACHE_DIRECTORY_EXPIRATION, DEFAULT_CACHE_DIRECTORY_EXPIRATION);
}
CLDAP::~CLDAP()
{
CloseServer();
if (NULL != m_hThread)
{
WARNING_OUT(("CLDAP - waiting for AsyncSearch Thread to exit (start)"));
WaitForSingleObject(m_hThread, 10000); // 10 seconds max
WARNING_OUT(("CLDAP - waiting for AsyncSearch to exit (end)"));
}
if( m_hSearchMutex )
{
CloseHandle( m_hSearchMutex );
}
// Free any cached data
while (!m_listDirCache.IsEmpty())
{
DIRCACHE * pDirCache = (DIRCACHE *) m_listDirCache.RemoveHead();
ASSERT(NULL != pDirCache);
FreeDirCache(pDirCache);
}
DbgMsg(iZONE_OBJECTS, "CLDAP - Destroyed(%08X)", this);
}
///////////////////////////////////////////////////////////////////////////
// CALV methods
/* S H O W I T E M S */
/*-------------------------------------------------------------------------
%%Function: ShowItems
-------------------------------------------------------------------------*/
VOID CLDAP::ShowItems(HWND hwnd)
{
DBGENTRY(CLDAP::ShowItems);
m_hWnd = hwnd;
SetWindow( hwnd ); // set in base class too...
CALV::ClearItems();
DisplayDirectory();
DBGEXIT(CLDAP::ShowItems);
}
/* C L E A R I T E M S */
/*-------------------------------------------------------------------------
%%Function: ClearItems
-------------------------------------------------------------------------*/
VOID CLDAP::ClearItems(void)
{
DBGENTRY( CLDAP::ClearItems );
StopSearch();
DBGEXIT( CLDAP::ClearItems );
}
/* virtual */
RAI * CLDAP::GetAddrInfo(void)
{
DBGENTRY(CLDAP::GetAddrInfo);
int selectedItem = CALV::GetSelection();
RAI * pRai = NULL;
if( selectedItem != -1 )
{
if( (pRai = new RAI) != NULL )
{
pRai->cItems = 1;
pRai->rgDwStr->dw = NM_ADDR_ULS;
GetSzName( pRai->szName, CCHMAX( pRai->szName ), selectedItem );
TCHAR buffer[ CCHMAXSZ ];
GetSzAddress( buffer, CCHMAX( buffer ),selectedItem );
pRai->rgDwStr->psz = PszAlloc( buffer );
}
}
DBGEXIT(CLDAP::GetAddrInfo);
return( pRai );
}
/* G E T S Z A D D R E S S */
/*-------------------------------------------------------------------------
%%Function: GetSzAddress
-------------------------------------------------------------------------*/
BOOL CLDAP::GetSzAddress(LPTSTR psz, int cchMax, int iItem)
{
DBGENTRY(CLDAP::GetSzAddress);
BOOL bRet = TRUE;
int cch = lstrlen(m_szServer);
if ((cch+2) < cchMax)
{
lstrcpy(psz, m_szServer);
psz += cch;
*psz++ = _T('/');
cchMax -= (cch+1);
bRet = CALV::GetSzData( psz, cchMax, iItem, COLUMN_INDEX_ADDRESS );
}
else
{
bRet = FALSE;
}
DBGEXIT(CLDAP::GetSzAddress);
return bRet;
}
/* O N C O M M A N D */
/*-------------------------------------------------------------------------
%%Function: OnCommand
-------------------------------------------------------------------------*/
VOID CLDAP::OnCommand(WPARAM wParam, LPARAM lParam)
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDM_DLGCALL_STOP:
StopSearch();
break;
case IDM_DLGCALL_REFRESH:
if (NULL == m_hThread)
{
CALV::ClearItems();
StartSearch();
}
break;
case IDM_DLGCALL_WAB:
CmdAddToWab();
break;
default:
CALV::OnCommand(wParam, lParam);
break;
}
}
/* C M D P R O P E R T I E S */
/*-------------------------------------------------------------------------
%%Function: CmdProperties
-------------------------------------------------------------------------*/
VOID CLDAP::CmdProperties(void)
{
DBGENTRY(CLDAP::CmdProperties);
DWORD dwThID;
HANDLE hThread = CreateThread(NULL, 0, _sAsyncPropertyThreadFn,
(LPVOID) this, 0, &dwThID);
CloseHandle(hThread);
DBGEXIT(CLDAP::CmdProperties);
}
DWORD CALLBACK CLDAP::_sAsyncPropertyThreadFn(LPVOID pv)
{
DBGENTRY(CLDAP::_sAsyncPropertyThreadFn);
CLDAP * pThis = (CLDAP *) pv;
ASSERT(NULL != pThis);
pThis->ShowProperties();
DBGEXIT(CLDAP::_sAsyncPropertyThreadFn);
return 0;
}
VOID CLDAP::ShowProperties(void)
{
DBGENTRY(CLDAP::ShowProperties);
SetBusyCursor(TRUE);
LDAPUSERDATA ldapUserData;
BOOL fOk = FGetUserData(&ldapUserData);
SetBusyCursor(FALSE);
if (!fOk)
return;
TCHAR szHasHardware[CCHMAXSZ];
FLoadString(IDS_HARDWARE_DETECTED, szHasHardware, CCHMAX(szHasHardware));
TCHAR szNoHardware[CCHMAXSZ];
FLoadString(IDS_NO_HARDWARE_DETECTED, szNoHardware, CCHMAX(szNoHardware));
UPROPDLGENTRY rgProp[] = {
{IDS_UPROP_EMAIL, ldapUserData.szEmail},
{IDS_UPROP_COMMENTS, ldapUserData.szComment},
{IDS_UPROP_VIDEO, ldapUserData.fVideoSend ? szHasHardware : szNoHardware},
{IDS_UPROP_AUDIO, ldapUserData.fAudioSend ? szHasHardware : szNoHardware},
{IDS_UPROP_VERSION, ldapUserData.szVersion},
};
CUserPropertiesDlg dlgUserProp(GetHwnd(), IDI_LARGE);
dlgUserProp.DoModal(rgProp, ARRAY_ELEMENTS(rgProp), ldapUserData.szName, NULL);
DBGEXIT(CLDAP::ShowProperties);
}
/* C M D A D D T O W A B */
/*-------------------------------------------------------------------------
%%Function: CmdAddToWab
-------------------------------------------------------------------------*/
VOID CLDAP::CmdAddToWab(void)
{
DBGENTRY(CLDAP::CmdAddToWab);
LDAPUSERDATA ldapUserData;
if (!FGetUserData(&ldapUserData))
return;
CreateWabEntry(ldapUserData.szName, ldapUserData.szFirst, ldapUserData.szLast, ldapUserData.szEmail,
NULL, NULL, ldapUserData.szComment, m_szServer);
DBGEXIT(CLDAP::CmdAddToWab);
}
///////////////////////////////////////////////////////////////////////////
/* S E T S E R V E R */
/*-------------------------------------------------------------------------
%%Function: SetServer
-------------------------------------------------------------------------*/
VOID CLDAP::SetServer(LPCTSTR pcszServer)
{
DBGENTRY(CLDAP::SetServer);
if ((0 != lstrcmpi(m_szServer, pcszServer)) || (DEFAULT_LDAP_PORT != m_ulPort))
{
CloseServer();
int cch;
LPCTSTR pszSeparator = _StrChr(pcszServer, _T(':'));
if (NULL != pszSeparator)
{
cch = (pszSeparator - pcszServer) + 1;
if (cch >= CCHMAX(m_szAddress))
return;
m_ulPort = DecimalStringToUINT(pszSeparator + 1);
}
else
{
cch = CCHMAX(m_szAddress);
m_ulPort = DEFAULT_LDAP_PORT;
}
// save the information before changing the server name
CacheServerData();
lstrcpyn(m_szAddress, pcszServer, cch);
lstrcpyn(m_szServer, pcszServer, CCHMAX(m_szServer));
}
DBGEXIT(CLDAP::SetServer);
}
/* D I R E C T O R Y S T A R T */
/*-------------------------------------------------------------------------
%%Function: DirectoryStart
Initiate directory request
-------------------------------------------------------------------------*/
VOID CLDAP::StartSearch(void)
{
DBGENTRY(CLDAP::StartSearch);
if (NULL == m_hThread)
{
DWORD dwThID;
AddRef();
m_bSearchCancelled = false;
HANDLE hThread = CreateThread(NULL, 0, _sAsyncSearchThreadFn,
(LPVOID) this, CREATE_SUSPENDED, &dwThID);
if (NULL != hThread)
{
m_hThread = hThread;
ResumeThread(hThread);
}
else
{
Release();
}
}
DBGEXIT(CLDAP::StartSearch);
}
DWORD CALLBACK CLDAP::_sAsyncSearchThreadFn(LPVOID pv)
{
DBGENTRY(CLDAP::_sAsyncSearchThreadFn);
CLDAP * pThis = (CLDAP *) pv;
ASSERT(NULL != pThis);
pThis->AsyncSearch();
pThis->Release();
DBGEXIT(CLDAP::_sAsyncSearchThreadFn);
return 0;
}
VOID CLDAP::AsyncSearch(void)
{
DBGENTRY(CLDAP::AsyncSearch);
HRESULT hr;
WARNING_OUT(("AsyncSearch Started"));
ASSERT(NULL != m_hThread);
SetBusyCursor(TRUE);
// We can not call EnableWindow() from this thread as it av's some of the time. This is a
// known windows bug. It also doesn't really solve the problem (#4726) it was put in here
// to solve anyway. We will work around this in a future build by re-architecting this so
// this little helper thread doesn't mess with the ui at all. It is problematic and very
// inefficient anyway.
// ::EnableWindow(GetDlgItem(GetParent(GetHwnd()), IDM_DLGCALL_REFRESH), FALSE);
m_fDirInProgress = TRUE;
m_dwTickStart = ::GetTickCount();
m_uniqueId = 0;
if( SUCCEEDED( WaitWithMessageLoop( m_hSearchMutex ) ) )
{
if (!FAvailable())
{
hr = E_ACCESSDENIED;
}
else if (!FOpenServer())
{
CloseServer();
hr = E_FAIL;
}
else
{
hr = DoQuery();
}
SetBusyCursor(FALSE); // This should be moved outside the if so it is always executed...
// We can not call EnableWindow() from this thread as it av's some of the time. This is a
// known windows bug. It also doesn't really solve the problem (#4726) it was put in here
// to solve anyway. We will work around this in a future build by re-architecting this so
// this little helper thread doesn't mess with the ui at all. It is problematic and very
// inefficient anyway.
// ::EnableWindow(GetDlgItem(GetParent(GetHwnd()), IDM_DLGCALL_REFRESH), TRUE);
HWND hwnd = GetHwnd();
if (-1 == ListView_GetNextItem(hwnd, -1, LVNI_ALL | LVNI_FOCUSED))
{
// No selection - set focus to the first item
ListView_SetItemState(hwnd, 0, LVIS_FOCUSED, LVIS_FOCUSED);
}
ReleaseMutex( m_hSearchMutex );
if (FAILED(hr))
{
PostMessage(GetParent(hwnd), WM_DISPLAY_MSG, IDS_ULSLOGON_ERROR, 0);
}
WARNING_OUT(("AsyncSearch Complete"));
// Close the thread handle safely
HANDLE hThread = m_hThread;
m_hThread = NULL;
CloseHandle(hThread);
}
if (m_fDirInProgress)
{
// Only cache if data took more than 2 seconds to retrieve
m_fIsCacheable = ((::GetTickCount() - m_dwTickStart) > 2000);
m_fDirInProgress = FALSE;
}
DBGEXIT(CLDAP::AsyncSearch);
}
///////////////////////////////////////////////////////////////////////////
/* F O P E N S E R V E R */
/*-------------------------------------------------------------------------
%%Function: FOpenServer
Make sure the connection to the LDAP server is open
-------------------------------------------------------------------------*/
BOOL CLDAP::FOpenServer(void)
{
DBGENTRY(CLDAP::FOpenServer);
BOOL bRet = TRUE;
if( NULL == m_pLdap )
{
if( m_bSearchCancelled )
{
bRet = FALSE;
}
else
{
WARNING_OUT(("Opening Server [%s] Port [%d]", m_szAddress, m_ulPort));
m_pLdap = WLDAP::ldap_open(m_szAddress, m_ulPort);
if( (m_pLdap == NULL) && (m_ulPort == DEFAULT_LDAP_PORT) )
{
m_pLdap = WLDAP::ldap_open(m_szAddress, ALTERNATE_LDAP_PORT); // Automatically retry with alternate port...
if( m_pLdap != NULL )
{
m_ulPort = ALTERNATE_LDAP_PORT;
}
}
if(NULL != m_pLdap)
{
LONG lTimeOut = 30; // seconds for timeout
WLDAP::ldap_set_option(m_pLdap, LDAP_OPT_TIMELIMIT, &lTimeOut);
// Defaults to ILS 2
ASSERT(LDAP_VERSION2 == m_pLdap->ld_version);
ULONG ulRet = WLDAP::ldap_simple_bind_s(m_pLdap, NULL, NULL);
WARNING_OUT(("Logon [%s] complete", m_szServer));
}
else
{
WARNING_OUT(("ldap_open err=%d", GetLastError()));
return FALSE;
}
}
}
DBGEXIT(CLDAP::FOpenServer);
return bRet;
}
/* C L O S E S E R V E R */
/*-------------------------------------------------------------------------
%%Function: CloseServer
-------------------------------------------------------------------------*/
VOID CLDAP::CloseServer(void)
{
DBGENTRY(CLDAP::CloseServer);
if(m_pLdap)
{
StopSearch();
if( SUCCEEDED( WaitWithMessageLoop( m_hSearchMutex ) ) )
{
WLDAP::ldap_unbind(m_pLdap);
m_pLdap = NULL;
ReleaseMutex( m_hSearchMutex );
}
WARNING_OUT(("Logoff [%s] complete", m_szServer));
}
}
/* G E T N E X T A T T R I B U T E */
/*-------------------------------------------------------------------------
%%Function: GetNextAttribute
-------------------------------------------------------------------------*/
LPTSTR CLDAP::GetNextAttribute(LPCTSTR pszExpect, LPTSTR psz, int cchMax, LPTSTR pszAttrib, LDAPMessage * pEntry, BerElement * pElement)
{
DBGENTRY(CLDAP::GetNextAttribute);
ASSERT(NULL != psz);
if ((NULL == pszAttrib) || (0 != lstrcmpi(pszAttrib, pszExpect)))
{
*psz = _T('\0');
}
else
{
LPTSTR * rgVal = WLDAP::ldap_get_values(m_pLdap, pEntry, pszAttrib);
if( rgVal[0] )
{
lstrcpyn(psz, rgVal[0], cchMax);
}
else
{
psz[0] = _T('\0');
}
WLDAP::ldap_value_free(rgVal);
pszAttrib = WLDAP::ldap_next_attribute(m_pLdap, pEntry, pElement);
}
DBGEXIT(CLDAP::GetNextAttribute);
return pszAttrib;
}
/* D O Q U E R Y */
/*-------------------------------------------------------------------------
%%Function: DoQuery
-------------------------------------------------------------------------*/
HRESULT CLDAP::DoQuery(void)
{
DBGENTRY(CLDAP::DoQuery);
HRESULT hr = S_FALSE;
ASSERT(FLoggedOn());
TCHAR szSearch[MAX_PATH];
ASSERT(CCHMAX(s_szSearchFormat3) + CCHMAX(s_szSearchCategory) < CCHMAX(szSearch));
ASSERT(CCHMAX(s_szSearchFormat2) + CCHMAX(s_szSearchCategory) < CCHMAX(szSearch));
if( !m_bSearchCancelled )
{
wsprintf(szSearch,
(LDAP_VERSION2 == m_pLdap->ld_version) ? s_szSearchFormat2 : s_szSearchFormat3,
(LDAP_VERSION2 == m_pLdap->ld_version) ? TEXT("%") : TEXT("*"), _T(""));
ASSERT(lstrlen(szSearch) < CCHMAX(szSearch));
ASSERT(0 == m_msgId); // one search at a time
m_msgId = WLDAP::ldap_search(m_pLdap, (PCHAR) "objectClass=RTPerson", LDAP_SCOPE_BASE,
szSearch, (PCHAR *) s_rgAttrNameAddr, 0);
if( m_msgId == -1 )
{
m_msgId = 0;
hr = E_FAIL;
}
else
{
SendMessage( m_hWnd, WM_SETREDRAW, FALSE, 0 );
while( (0 != m_msgId) && (!m_bSearchCancelled) )
{
LDAP_TIMEVAL timeOut = {2, 0};
LDAPMessage * pResult = NULL;
WARNING_OUT(("DoQuery calling ldap_result"));
int iResult = WLDAP::ldap_result(m_pLdap, m_msgId, 0, &timeOut, &pResult);
WARNING_OUT( ("DoQuery back from ldap_result: 0x%08X", iResult) );
if (0 == iResult)
{
SendMessage( m_hWnd, WM_SETREDRAW, TRUE, 0 );
UpdateWindow( m_hWnd );
SendMessage( m_hWnd, WM_SETREDRAW, FALSE, 0 );
continue;
}
if (LDAP_RES_SEARCH_ENTRY != iResult)
{
// S_FALSE = abandoned
// hr = (-1 == iResult) ? S_OK : S_FALSE;
hr = (-1 == iResult) ? E_FAIL : S_FALSE;
break;
}
AddEntries(pResult);
WLDAP::ldap_msgfree(pResult);
}
SendMessage( m_hWnd, WM_SETREDRAW, TRUE, 0 );
m_msgId = 0;
DirComplete( TRUE );
forceSort();
}
}
DBGEXIT(CLDAP::DoQuery);
return hr;
}
/* A D D E N T R I E S */
/*-------------------------------------------------------------------------
%%Function: AddEntries
Add the Entries to the listbox. (Data ordered by s_rgAttrNameAddr.)
-------------------------------------------------------------------------*/
VOID CLDAP::AddEntries(LDAPMessage * pResult)
{
DBGENTRY(CLDAP::AddEntries);
LDAPMessage * pEntry;
BerElement * pElement;
for (pEntry = WLDAP::ldap_first_entry(m_pLdap, pResult);
NULL != pEntry;
pEntry = WLDAP::ldap_next_entry(m_pLdap, pEntry))
{
LPTSTR pszAttrib = WLDAP::ldap_first_attribute(m_pLdap, pEntry, &pElement);
if (NULL == pszAttrib)
break;
// Must have a "Show Me" attribute
if (0 != lstrcmpi(pszAttrib, s_cszAttribShow))
continue;
LPTSTR * rgVal = WLDAP::ldap_get_values(m_pLdap, pEntry, pszAttrib);
BOOL fShow = (_T('1') == *rgVal[0]);
WLDAP::ldap_value_free(rgVal);
if (!fShow)
continue;
pszAttrib = WLDAP::ldap_next_attribute(m_pLdap, pEntry, pElement);
TCHAR szEmail[CCHMAXSZ_EMAIL];
pszAttrib = GetNextAttribute(s_cszAttribEmail, szEmail, CCHMAX(szEmail),
pszAttrib, pEntry, pElement);
TCHAR szFirstName[CCHMAXSZ_FIRSTNAME];
pszAttrib = GetNextAttribute(s_cszAttribFirstName, szFirstName, CCHMAX(szFirstName),
pszAttrib, pEntry, pElement);
TCHAR szLastName[CCHMAXSZ_LASTNAME];
pszAttrib = GetNextAttribute(s_cszAttribLastName, szLastName, CCHMAX(szLastName),
pszAttrib, pEntry, pElement);
TCHAR szLocation[CCHMAXSZ_LASTNAME];
pszAttrib = GetNextAttribute(s_cszAttribLocation, szLocation, CCHMAX(szLocation),
pszAttrib, pEntry, pElement);
TCHAR szTemp[4];
pszAttrib = GetNextAttribute(s_cszAttribInACall, szTemp, CCHMAX(szTemp),
pszAttrib, pEntry, pElement);
int iInCallImage = (szTemp[0] == _T('1')) ? II_IN_A_CALL : II_COMPUTER;
pszAttrib = GetNextAttribute(s_cszAttribAudio, szTemp, CCHMAX(szTemp),
pszAttrib, pEntry, pElement);
int iAudioImage = (szTemp[0] == _T('1')) ? II_AUDIO_CAPABLE : 0;
pszAttrib = GetNextAttribute(s_cszAttribVideo, szTemp, CCHMAX(szTemp),
pszAttrib, pEntry, pElement);
int iVideoImage = (szTemp[0] == _T('1')) ? II_VIDEO_CAPABLE : 0;
TCHAR szComment[CCHMAXSZ_COMMENT];
pszAttrib = GetNextAttribute(s_cszAttribComment, szComment, CCHMAX(szComment),
pszAttrib, pEntry, pElement);
lvAddItem( 0, iInCallImage, iAudioImage, iVideoImage, szEmail, szFirstName, szLastName, szLocation, szComment );
}
DBGEXIT(CLDAP::AddEntries);
}
//--------------------------------------------------------------------------//
// CLDAP::lvAddItem. //
//--------------------------------------------------------------------------//
int
CLDAP::lvAddItem
(
int item,
int iInCallImage,
int iAudioImage,
int iVideoImage,
LPCTSTR address,
LPCTSTR firstName,
LPCTSTR lastName,
LPCTSTR location,
LPCTSTR comments
){
LV_ITEM lvItem;
ClearStruct( &lvItem );
lvItem.mask = LVIF_PARAM;
lvItem.iItem = item;
lvItem.lParam = m_uniqueId++; // assign a unique lParam for this item
int index = ListView_InsertItem( m_hWnd, &lvItem );
if( index != -1 )
{
if( lastName )
{
ListView_SetItemText( m_hWnd, index, COLUMN_INDEX_LAST_NAME, (LPTSTR) lastName );
}
if( firstName )
{
ListView_SetItemText( m_hWnd, index, COLUMN_INDEX_FIRST_NAME, (LPTSTR) firstName );
}
if( iAudioImage != 0 )
{
lvItem.mask = LVIF_IMAGE;
lvItem.iSubItem = COLUMN_INDEX_AUDIO;
lvItem.iImage = iAudioImage;
ListView_SetItem( m_hWnd, &lvItem );
}
if( iVideoImage != 0 )
{
lvItem.mask = LVIF_IMAGE;
lvItem.iSubItem = COLUMN_INDEX_VIDEO;
lvItem.iImage = iVideoImage;
ListView_SetItem( m_hWnd, &lvItem );
}
if( address )
{
lvItem.mask = LVIF_IMAGE | LVIF_TEXT;
lvItem.iSubItem = COLUMN_INDEX_ADDRESS;
lvItem.iImage = iInCallImage;
lvItem.pszText = (LPTSTR) address;
lvItem.cchTextMax = lstrlen( lvItem.pszText );
ListView_SetItem( m_hWnd, &lvItem );
}
if( location )
{
ListView_SetItemText( m_hWnd, index, COLUMN_INDEX_LOCATION, (LPTSTR) location );
}
if( comments )
{
ListView_SetItemText( m_hWnd, index, COLUMN_INDEX_COMMENTS, (LPTSTR) comments );
}
}
return( index );
} // End of CLDAP::lvAddItem.
/* S T O P S E A R C H */
/*-------------------------------------------------------------------------
%%Function: StopSearch
-------------------------------------------------------------------------*/
VOID CLDAP::StopSearch(void)
{
DBGENTRY(CLDAP::StopSearch);
m_bSearchCancelled = true;
if (0 != m_msgId)
{
// Dont call ldap_abandon() from this thread as the search thread may be inside ldap_result()
// at the time. For now just set m_msgId to zero and the search thread will stop when ldap_result()
// returns.
// ULONG uResult = WLDAP::ldap_abandon(m_pLdap, m_msgId);
WARNING_OUT(("Stopping Search..."));
m_msgId = 0;
m_fDirInProgress = FALSE;
m_fIsCacheable = FALSE;
DirComplete(FALSE);
}
DBGEXIT(CLDAP::StopSearch);
}
/* F G E T U S E R D A T A */
/*-------------------------------------------------------------------------
%%Function: FGetUserData
Get the data for a single user.
-------------------------------------------------------------------------*/
BOOL CLDAP::FGetUserData(LDAPUSERDATA * pLdapUserData)
{
DBGENTRY(CLDAP::FGetUserData);
ClearStruct(pLdapUserData);
if (!FLoggedOn())
{
DBGEXIT(CLDAP::FGetUserData);
return FALSE;
}
int iItem = CALV::GetSelection();
if (-1 == iItem)
{
DBGEXIT(CLDAP::FGetUserData);
return FALSE;
}
if (!GetSzData(pLdapUserData->szEmail, CCHMAX(pLdapUserData->szEmail), iItem, COLUMN_INDEX_ADDRESS))
{
DBGEXIT(CLDAP::FGetUserData);
return FALSE;
}
TCHAR szSearch[CCHMAX(s_szSearchFormat2) + CCHMAXSZ_EMAIL];
wsprintf(szSearch,
(LDAP_VERSION2 == m_pLdap->ld_version) ? s_szSearchFormat2 : s_szSearchFormat3,
pLdapUserData->szEmail, g_cszEmpty);
ASSERT(lstrlen(szSearch) < CCHMAX(szSearch));
LDAPMessage * pResult = NULL;
ULONG ulRet = WLDAP::ldap_search_s(m_pLdap, (LPTSTR) "objectClass=RTPerson", LDAP_SCOPE_BASE,
szSearch, (PCHAR *) s_rgAttrAll, FALSE, &pResult);
if (LDAP_SUCCESS != ulRet)
{
WLDAP::ldap_msgfree(pResult);
WARNING_OUT(("ldap_search (code=%08X)", ulRet));
DBGEXIT(CLDAP::FGetUserData);
return FALSE;
}
LDAPMessage * pEntry = WLDAP::ldap_first_entry(m_pLdap, pResult);
if (NULL != pEntry)
{
BerElement * pElement;
LPTSTR pszAttrib = WLDAP::ldap_first_attribute(m_pLdap, pEntry, &pElement);
// Make sure that the first attribute is s_cszAttribFirstName...
if ((NULL != pszAttrib) && (0 == lstrcmpi(pszAttrib, s_cszAttribFirstName)))
{
LPTSTR * rgVal = WLDAP::ldap_get_values(m_pLdap, pEntry, pszAttrib);
WLDAP::ldap_value_free(rgVal);
// pszAttrib = WLDAP::ldap_next_attribute(m_pLdap, pEntry, pElement);
pszAttrib = GetNextAttribute(s_cszAttribFirstName,
pLdapUserData->szFirst, CCHMAX(pLdapUserData->szFirst),
pszAttrib, pEntry, pElement);
pszAttrib = GetNextAttribute(s_cszAttribLastName,
pLdapUserData->szLast, CCHMAX(pLdapUserData->szLast),
pszAttrib, pEntry, pElement);
CombineNames(pLdapUserData->szName, CCHMAX(pLdapUserData->szName),
pLdapUserData->szFirst, pLdapUserData->szLast);
pszAttrib = GetNextAttribute(s_cszAttribComment,
pLdapUserData->szComment, CCHMAX(pLdapUserData->szComment),
pszAttrib, pEntry, pElement);
TCHAR szTemp[4];
pszAttrib = GetNextAttribute(s_cszAttribAudio, szTemp, CCHMAX(szTemp),
pszAttrib, pEntry, pElement);
pLdapUserData->fAudioSend = _T('1') == szTemp[0];
pszAttrib = GetNextAttribute(s_cszAttribVideo, szTemp, CCHMAX(szTemp),
pszAttrib, pEntry, pElement);
pLdapUserData->fVideoSend = _T('1') == szTemp[0];
pszAttrib = GetNextAttribute(s_cszAttribVersion,
pLdapUserData->szVersion, CCHMAX(pLdapUserData->szVersion),
pszAttrib, pEntry, pElement);
pszAttrib = GetNextAttribute(s_cszAttribCategory, szTemp, CCHMAX(szTemp),
pszAttrib, pEntry, pElement);
ConvertVersionInfo(pLdapUserData->szVersion, szTemp);
}
}
WLDAP::ldap_msgfree(pResult);
DBGEXIT(CLDAP::FGetUserData);
return TRUE;
}
/* C O N V E R T V E R S I O N I N F O */
/*-------------------------------------------------------------------------
%%Function: ConvertVersionInfo
-------------------------------------------------------------------------*/
VOID ConvertVersionInfo(LPTSTR pszVersion, LPTSTR pszCategory)
{
UINT uVer = DecimalStringToUINT(pszVersion);
if (0 == uVer)
{
lstrcpy(pszVersion, FEmptySz(pszCategory) ? TEXT("1.0") : TEXT("2"));
}
else
{
LPCTSTR pszRel;
switch (LOBYTE(HIWORD(uVer)))
{
case 3: pszRel = TEXT("2.11"); break;
case 4: pszRel = TEXT("3.0"); break;
case 5: pszRel = TEXT("4.0"); break;
default: pszRel = g_cszEmpty; break;
}
TCHAR szFormat[CCHMAXSZ];
FLoadString(IDS_FORMAT_VERSION, szFormat, CCHMAX(szFormat));
wsprintf(pszVersion, szFormat, pszRel,
HIBYTE(HIWORD(uVer)), LOBYTE(HIWORD(uVer)), LOWORD(uVer));
}
}
//--------------------------------------------------------------------------//
// CLDAP::FreeDirCache. //
//--------------------------------------------------------------------------//
void
CLDAP::FreeDirCache
(
DIRCACHE * pDirCache
){
TRACE_OUT(("FreeDirCache [%s] filter=%d, expire=%d",
pDirCache->pszServer, pDirCache->dwTickExpire));
ASSERT(NULL != pDirCache);
PBYTE pb = pDirCache->pData;
delete pDirCache->pszServer;
delete pDirCache;
while (NULL != pb)
{
PBYTE pTemp = pb;
pb = (PBYTE) * (DWORD_PTR *) pb;
delete pTemp;
}
} // End of CLDAP::FreeDirCache.
//--------------------------------------------------------------------------//
// CLDAP::FreeDirCache. //
//--------------------------------------------------------------------------//
void
CLDAP::DirComplete
(
bool //fPostUiUpdate
){
if( m_fDirInProgress )
{
// Only cache if data took more than 2 seconds to retrieve
m_fIsCacheable = ((::GetTickCount() - m_dwTickStart) > 2000);
m_fDirInProgress = FALSE;
}
m_cTotalEntries = 0;
m_cEntries = 0;
} // End of CLDAP:DirComplete.
//--------------------------------------------------------------------------//
// CLDAP::GetSzName. //
//--------------------------------------------------------------------------//
BOOL
CLDAP::GetSzName
(
LPTSTR psz,
int cchMax,
int iItem
){
TCHAR szOrder[ MAX_PATH ];
bool bFirstNameFirst = ((::LoadString( ::GetInstanceHandle(), IDS_NAME_ORDER, szOrder, CCHMAX(szOrder)) == 0) ||
(_T( '1' ) == szOrder[ 1 ]));
int iFirst = bFirstNameFirst? COLUMN_INDEX_FIRST_NAME: COLUMN_INDEX_LAST_NAME;
int iLast = bFirstNameFirst? COLUMN_INDEX_LAST_NAME: COLUMN_INDEX_FIRST_NAME;
GetSzData( psz, cchMax, iItem, iFirst );
int length = lstrlen( psz );
if( (length > 0) && (length < cchMax - 1) )
{
lstrcat( psz, TEXT( " " ) );
length++;
}
GetSzData( &psz[ length ], cchMax - length, iItem, iLast );
return( lstrlen( psz ) > 0 );
} // End of CLDAP::GetSzName.
static const int CDIRCACHE_SZ = 5; // number of strings
static const int CDIRCACHE_IMAGE = 3; // number of images
//--------------------------------------------------------------------------//
// CLDAP::CacheServerData. //
//--------------------------------------------------------------------------//
void
CLDAP::CacheServerData(void)
{
DIRCACHE * pDirCache;
if (!m_fCacheDirectory)
return; // User disabled directory caching
if (!m_fIsCacheable)
{
TRACE_OUT(("CacheServerData: not caching [%s]", m_szServer));
return;
}
if (m_fDirInProgress)
return; // don't cache partial data
// Remove any previous cached data
POSITION pos = FindCachedData();
if (NULL != pos)
{
pDirCache = (DIRCACHE *) m_listDirCache.RemoveAt(pos);
ASSERT(NULL != pDirCache);
FreeDirCache(pDirCache);
}
DWORD dwTickExpire = m_dwTickStart + (m_cMinutesExpire * 60000);
if (dwTickExpire < GetTickCount())
{
TRACE_OUT(("CacheServerData: [%s] data has expired", m_szServer));
return; // data is already expired
}
int cItems = ListView_GetItemCount(m_hWnd);
if (0 == cItems)
return; // nothing to cache
pDirCache = new DIRCACHE;
if (NULL == pDirCache)
return;
pDirCache->pszServer = PszAlloc(m_szServer);
pDirCache->dwTickExpire = dwTickExpire;
pDirCache->pData = NULL;
LPTSTR pPrev = (LPTSTR) &pDirCache->pData;
m_listDirCache.AddTail(pDirCache);
LV_ITEM lvi;
lvi.cchTextMax = MAX_PATH;
for (lvi.iItem = 0; lvi.iItem < cItems; lvi.iItem++)
{
int iInCallImage = -1;
int iAudioImage = -1;
int iVideoImage = -1;
int i = 0; // index into rgcb, rgsz
int cb = 0; // total length of string data
int rgcb[CDIRCACHE_SZ]; // size of each string
TCHAR rgsz[CDIRCACHE_SZ][MAX_PATH]; // string buffers
// Get the string data for each column
lvi.mask = LVIF_IMAGE;
for (lvi.iSubItem = 0; lvi.iSubItem < MAX_DIR_COLUMNS; lvi.iSubItem++)
{
if( (lvi.iSubItem != COLUMN_INDEX_AUDIO) && (lvi.iSubItem != COLUMN_INDEX_VIDEO) )
{
lvi.mask |= LVIF_TEXT;
lvi.pszText = rgsz[i];
ListView_GetItem(m_hWnd, &lvi);
rgcb[i] = lstrlen(lvi.pszText);
cb += rgcb[i] + 1; // Plus one for the NULL...
i++;
if( lvi.iSubItem == COLUMN_INDEX_ADDRESS )
{
iInCallImage = lvi.iImage;
}
}
else
{
lvi.mask &= ~LVIF_TEXT;
ListView_GetItem(m_hWnd, &lvi);
if( lvi.iSubItem == COLUMN_INDEX_AUDIO )
{
iAudioImage = lvi.iImage;
}
else if( lvi.iSubItem == COLUMN_INDEX_VIDEO)
{
iVideoImage = lvi.iImage;
}
}
}
// allocate space for: a link (DWORD), strings (cb), images (CDIRCACHE_IMAGES)
PBYTE pData = new BYTE[ sizeof(DWORD_PTR) + cb + CDIRCACHE_IMAGES ];
if (NULL == pData)
{
// can't hold all data - give up and return
ClearServerCache();
return;
}
* ((DWORD_PTR *) pData) = 0; // link to next item is null
PBYTE pb = pData + sizeof(DWORD_PTR);
// copy the string data into the buffer
for (i = 0; i < CDIRCACHE_SZ; i++)
{
lstrcpy((LPTSTR) pb, rgsz[i]);
pb += rgcb[i] + 1;
}
*pb++ = (BYTE) iInCallImage;
*pb++ = (BYTE) iAudioImage;
*pb = (BYTE) iVideoImage;
* ((DWORD_PTR *) pPrev) = (DWORD_PTR) pData; // link into the previous data
pPrev = (LPTSTR) pData;
}
TRACE_OUT(("CacheServerData: [%s] expire=%d",
m_szServer, dwTickExpire));
} // End of CLDAP::CacheServerData.
//--------------------------------------------------------------------------//
// CLDAP::FindCachedData. //
//--------------------------------------------------------------------------//
POSITION
CLDAP::FindCachedData(void)
{
DWORD dwTick = GetTickCount();
TRACE_OUT(("Searching for cached data on [%s]", m_szServer));
POSITION pos = m_listDirCache.GetHeadPosition();
while (NULL != pos)
{
POSITION posSav = pos;
DIRCACHE * pDirCache = (DIRCACHE *) m_listDirCache.GetNext(pos);
ASSERT(NULL != pDirCache);
if ((0 == lstrcmp(m_szServer, pDirCache->pszServer)) &&
(pDirCache->dwTickExpire > dwTick))
{
return posSav;
}
}
return NULL;
} // End of CLDAP::FindCachedData.
//--------------------------------------------------------------------------//
// CLDAP::ClearServerCache. //
//--------------------------------------------------------------------------//
void
CLDAP::ClearServerCache(void)
{
DWORD dwTick = GetTickCount();
POSITION pos = m_listDirCache.GetHeadPosition();
while (NULL != pos)
{
POSITION posSav = pos;
DIRCACHE * pDirCache = (DIRCACHE *) m_listDirCache.GetNext(pos);
ASSERT(NULL != pDirCache);
if ( (0 == lstrcmp(m_szServer, pDirCache->pszServer)) || (pDirCache->dwTickExpire < dwTick) )
{
m_listDirCache.RemoveAt(posSav);
FreeDirCache(pDirCache);
}
#ifdef DEBUG
else
{
TRACE_OUT(("Keeping cached data for [%s] , expire=%d",
pDirCache->pszServer, pDirCache->dwTickExpire));
}
#endif /* DEBUG */
}
} // End of CLDAP::ClearServerCache.
//--------------------------------------------------------------------------//
// CLDAP::DisplayDirectory. //
//--------------------------------------------------------------------------//
void
CLDAP::DisplayDirectory(void)
{
POSITION pos = FindCachedData();
if (NULL == pos)
{
// no cached information - request new data
StartSearch();
return;
}
DIRCACHE * pDirCache = (DIRCACHE *) m_listDirCache.GetFromPosition(pos);
ASSERT(NULL != pDirCache);
LPTSTR pDirLine = (LPTSTR) pDirCache->pData;
StopSearch(); // In case the previous server is slow
CALV::ClearItems();
m_fIsCacheable = FALSE; // don't bother attempting to re-cache this data
// Restore the cached server information
TRACE_OUT(("Restoring cached data for [%s] expire=%d",
m_szServer, pDirCache->dwTickExpire));
SendMessage( m_hWnd, WM_SETREDRAW, FALSE, 0 );
while (NULL != pDirLine)
{
DWORD_PTR *pNext = * (DWORD_PTR * * ) pDirLine; pDirLine += sizeof(DWORD_PTR);
LPTSTR pszEmail = (LPTSTR) pDirLine; pDirLine += lstrlen(pDirLine)+1;
LPTSTR pszLast = (LPTSTR) pDirLine; pDirLine += lstrlen(pDirLine)+1;
LPTSTR pszFirst = (LPTSTR) pDirLine; pDirLine += lstrlen(pDirLine)+1;
LPTSTR pszLocation = (LPTSTR) pDirLine; pDirLine += lstrlen(pDirLine)+1;
LPTSTR pszComment = (LPTSTR) pDirLine; pDirLine += lstrlen(pDirLine)+1;
int iiCall = (int) * (char *) pDirLine; pDirLine++;
int iiAudio = (int) * (char *) pDirLine; pDirLine++;
int iiVideo = (int) * (char *) pDirLine;
lvAddItem( 0, iiCall, iiAudio, iiVideo, pszEmail, pszFirst, pszLast, pszLocation, pszComment );
pDirLine = (LPTSTR) pNext;
}
forceSort();
SendMessage( m_hWnd, WM_SETREDRAW, TRUE, 0 );
} // End of CLDAP::DisplayDirectory.
//--------------------------------------------------------------------------//
// CLDAP::forceSort. //
//--------------------------------------------------------------------------//
void
CLDAP::forceSort(void)
{
NM_LISTVIEW nmlv;
nmlv.hdr.code = LVN_COLUMNCLICK;
nmlv.hdr.hwndFrom = m_hWnd;
nmlv.iSubItem = -1; // default sort column...
SendMessage( GetParent( m_hWnd ), WM_NOTIFY, GetDlgCtrlID( m_hWnd ), (LPARAM) &nmlv );
} // End of CLDAP::forceSort.
//--------------------------------------------------------------------------//
// CLDAP::GetIconId. //
//--------------------------------------------------------------------------//
int
CLDAP::GetIconId(LPCTSTR psz)
{
return( CDirectoryManager::isWebDirectory( psz )? II_WEB_DIRECTORY: CALV::GetIconId( NULL ) );
} // End of CLDAP::GetIconId.