Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2118 lines
65 KiB

  1. // File: GAL.cpp
  2. #include "precomp.h"
  3. #include "resource.h"
  4. #include "help_ids.h"
  5. #include "dirutil.h"
  6. #include "GAL.h"
  7. #include "MapiInit.h"
  8. #include "AdLkup.h"
  9. #include <lst.h>
  10. #define NM_INVALID_MAPI_PROPERTY 0
  11. // Registry Stuff
  12. /* static */ LPCTSTR CGAL::msc_szDefaultILSServerRegKey = ISAPI_CLIENT_KEY;
  13. /* static */ LPCTSTR CGAL::msc_szDefaultILSServerValue = REGVAL_SERVERNAME;
  14. /* static */ LPCTSTR CGAL::msc_szNMPolRegKey = POLICIES_KEY;
  15. /* static */ LPCTSTR CGAL::msc_szNMExchangeAtrValue = REGVAL_POL_NMADDRPROP;
  16. /* static */ LPCTSTR CGAL::msc_szSMTPADDRESSNAME = TEXT( "SMTP" );
  17. // If there is no DISPLAY_NAME or ACCOUNT_NAME, don't display anything
  18. /* static */ LPCTSTR CGAL::msc_szNoDisplayName = TEXT( "" );
  19. /* static */ LPCTSTR CGAL::msc_szNoEMailName = TEXT( "" );
  20. /* static */ LPCTSTR CGAL::msc_szNoBusinessTelephoneNum = TEXT( "" );
  21. // Async stuff - there is only one instance of the GAL thread
  22. /* static */ HINSTANCE CGAL::m_hInstMapi32DLL = NULL;
  23. /* static */ HANDLE CGAL::m_hEventEndAsyncThread = NULL;
  24. /* static */ HANDLE CGAL::m_hAsyncLogOntoGalThread = NULL;
  25. /* static */ CGAL::eAsyncLogonState CGAL::m_AsyncLogonState = CGAL::AsyncLogonState_Idle;
  26. /* static */ IAddrBook * CGAL::m_pAddrBook = NULL;
  27. /* static */ IMAPITable * CGAL::m_pContentsTable = NULL;
  28. /* static */ IMAPIContainer * CGAL::m_pGAL = NULL;
  29. /* static */ ULONG CGAL::m_nRows = 0;
  30. static const int _rgIdMenu[] = {
  31. IDM_DLGCALL_SPEEDDIAL,
  32. 0
  33. };
  34. CGAL::CGAL() :
  35. CALV(IDS_DLGCALL_GAL, II_GAL, _rgIdMenu, true ),
  36. m_nBlockSize( DefaultBlockSize ),
  37. m_MaxCacheSize( DefaultMaxCacheSize ),
  38. m_bBeginningBookmarkIsValid( false ),
  39. m_bEndBookmarkIsValid( false ),
  40. m_hrGALError( S_OK ),
  41. m_hWndListView(NULL)
  42. {
  43. DbgMsg(iZONE_OBJECTS, "CGAL - Constructed(%08X)", this);
  44. _ResetCache();
  45. msc_ErrorEntry_NoGAL = CGalEntry();
  46. if (NULL == m_hInstMapi32DLL)
  47. {
  48. WARNING_OUT(("MAPI32.dll was not loaded?"));
  49. return;
  50. }
  51. //////////////////////////////////////////////////////////////////////////////////////////
  52. // We have to see if the GAL is available..
  53. // this is modified from Q188482 and Q171636
  54. //////////////////////////////////////////////////////////////////////////////////////////
  55. // first we have to initialize MAPI for this ( the main ) thread...
  56. MAPIINIT_0 mi = { MAPI_INIT_VERSION, MAPI_MULTITHREAD_NOTIFICATIONS };
  57. TRACE_OUT(("Initializing MAPI"));
  58. HRESULT hr = lpfnMAPIInitialize(&mi);
  59. if( SUCCEEDED( hr ) )
  60. {
  61. TRACE_OUT(("MAPI Initialized"));
  62. // We have to get a pointer to the AdminProfile which is basically
  63. // a manipulator for the mapisvc.inf file that should be on user's computer
  64. LPPROFADMIN pAdminProfiles = NULL;
  65. hr = lpfnMAPIAdminProfiles( 0L, &pAdminProfiles );
  66. if( SUCCEEDED( hr ) )
  67. { ASSERT( pAdminProfiles );
  68. // Get the profile table to search for the default profile
  69. LPMAPITABLE pProfTable = NULL;
  70. hr = pAdminProfiles->GetProfileTable( 0L, &pProfTable );
  71. if( SUCCEEDED( hr ) )
  72. { ASSERT( pProfTable );
  73. // Set the restriction to search for the default profile
  74. SRestriction Restriction;
  75. SPropValue spv;
  76. Restriction.rt = RES_PROPERTY;
  77. Restriction.res.resProperty.relop = RELOP_EQ;
  78. Restriction.res.resProperty.ulPropTag = PR_DEFAULT_PROFILE;
  79. Restriction.res.resProperty.lpProp = &spv;
  80. spv.ulPropTag = PR_DEFAULT_PROFILE;
  81. spv.Value.b = TRUE;
  82. // Find the default profile....
  83. hr = pProfTable->FindRow( &Restriction, BOOKMARK_BEGINNING, 0 );
  84. if( SUCCEEDED( hr ) )
  85. {
  86. // We have a default profile
  87. LPSRowSet pRow = NULL;
  88. hr = pProfTable->QueryRows( 1, 0, &pRow );
  89. if( SUCCEEDED( hr ) )
  90. { ASSERT( pRow );
  91. // The profile table entry really should have only two properties,
  92. // We will simply enumerate the proprtiies instead of hard-coding the
  93. // order of the properties ( in case it changes in the future )
  94. // PR_DISPLAY_NAME and PR_DEFAULT_PROFILE
  95. for( UINT iCur = 0; iCur < pRow->aRow->cValues; ++iCur )
  96. {
  97. // We are only interested in the PR_DISPLAY_NAME property
  98. if( pRow->aRow->lpProps[iCur].ulPropTag == PR_DISPLAY_NAME )
  99. {
  100. // Now that we have the default profile, we want to get the
  101. // profile admin interface for this profile
  102. LPSERVICEADMIN pSvcAdmin = NULL; // Pointer to IServiceAdmin object
  103. hr = pAdminProfiles->AdminServices( pRow->aRow->lpProps[iCur].Value.LPSZ,
  104. NULL,
  105. 0L,
  106. 0L,
  107. &pSvcAdmin
  108. );
  109. if( SUCCEEDED( hr ) )
  110. { ASSERT( pSvcAdmin );
  111. LPMAPITABLE pSvcTable = NULL;
  112. if( SUCCEEDED( hr = pSvcAdmin->GetMsgServiceTable( 0L, &pSvcTable ) ) )
  113. { ASSERT( pSvcTable );
  114. enum {iSvcName, iSvcUID, cptaSvc};
  115. SizedSPropTagArray (cptaSvc, sptCols) = { cptaSvc,
  116. PR_SERVICE_NAME,
  117. PR_SERVICE_UID };
  118. Restriction.rt = RES_PROPERTY;
  119. Restriction.res.resProperty.relop = RELOP_EQ;
  120. Restriction.res.resProperty.ulPropTag = PR_SERVICE_NAME;
  121. Restriction.res.resProperty.lpProp = &spv;
  122. spv.ulPropTag = PR_SERVICE_NAME;
  123. spv.Value.LPSZ = _T("MSEMS");
  124. LPSRowSet pRowExch = NULL;
  125. if ( SUCCEEDED( hr = lpfnHrQueryAllRows( pSvcTable,
  126. (LPSPropTagArray)&sptCols,
  127. &Restriction,
  128. NULL,
  129. 0,
  130. &pRowExch ) ) )
  131. {
  132. SetAvailable(TRUE);
  133. lpfnFreeProws( pRowExch );
  134. iCur = pRow->aRow->cValues;
  135. }
  136. pSvcTable->Release();
  137. pSvcTable = NULL;
  138. }
  139. pSvcAdmin->Release();
  140. pSvcAdmin = NULL;
  141. }
  142. }
  143. }
  144. lpfnFreeProws( pRow );
  145. }
  146. }
  147. pProfTable->Release();
  148. pProfTable = NULL;
  149. }
  150. pAdminProfiles->Release();
  151. pAdminProfiles = NULL;
  152. }
  153. lpfnMAPIUninitialize();
  154. }
  155. m_MaxJumpSize = m_nBlockSize;
  156. }
  157. CGAL::~CGAL()
  158. {
  159. // Kill the cache
  160. _ResetCache();
  161. DbgMsg(iZONE_OBJECTS, "CGAL - Destroyed(%08X)", this);
  162. }
  163. // static function to load MAPI32.dll
  164. BOOL CGAL::FLoadMapiFns(void)
  165. {
  166. if (NULL != m_hInstMapi32DLL)
  167. return TRUE;
  168. return LoadMapiFns(&m_hInstMapi32DLL);
  169. }
  170. // static function to unload MAPI32.dll and logoff, if necessary
  171. VOID CGAL::UnloadMapiFns(void)
  172. {
  173. if (NULL != m_hAsyncLogOntoGalThread)
  174. {
  175. TRACE_OUT(("Setting AsyncLogOntoGalThread End Event"));
  176. ASSERT(NULL != m_hEventEndAsyncThread);
  177. SetEvent(m_hEventEndAsyncThread);
  178. WARNING_OUT(("Waiting for AsyncLogOntoGalThread to exit (start)"));
  179. WaitForSingleObject(m_hAsyncLogOntoGalThread, 30000); // 30 seconds max
  180. WARNING_OUT(("Waiting for AsyncLogOntoGalThread to exit (end)"));
  181. CloseHandle(m_hAsyncLogOntoGalThread);
  182. m_hAsyncLogOntoGalThread = NULL;
  183. CloseHandle(m_hEventEndAsyncThread);
  184. m_hEventEndAsyncThread = NULL;
  185. }
  186. if (NULL != m_hInstMapi32DLL)
  187. {
  188. FreeLibrary(m_hInstMapi32DLL);
  189. m_hInstMapi32DLL = NULL;
  190. }
  191. }
  192. /* virtual */ int CGAL::OnListGetImageForItem( int iIndex ) {
  193. if( !_IsLoggedOn() )
  194. {
  195. return II_INVALIDINDEX;
  196. }
  197. CGalEntry* pEntry = _GetEntry( iIndex );
  198. if( pEntry->GetDisplayType() == DT_MAILUSER ) { return II_INVALIDINDEX; }
  199. switch( pEntry->GetDisplayType() ) {
  200. case DT_DISTLIST: return II_DISTLIST;
  201. case DT_FORUM: return II_FORUM;
  202. case DT_AGENT: return II_AGENT;
  203. case DT_ORGANIZATION: return II_ORGANIZATION;
  204. case DT_PRIVATE_DISTLIST: return II_PRIVATE_DISTLIST;
  205. case DT_REMOTE_MAILUSER: return II_REMOTE_MAILUSER;
  206. default:
  207. ERROR_OUT(("We have an invalid Display Type"));
  208. return II_INVALIDINDEX;
  209. }
  210. return II_INVALIDINDEX;
  211. }
  212. /* virtual */ bool CGAL::IsItemBold( int index ) {
  213. if( !_IsLoggedOn() )
  214. {
  215. return false;
  216. }
  217. CGalEntry* pEntry = _GetEntry( index );
  218. switch( pEntry->GetDisplayType() ) {
  219. case DT_DISTLIST:
  220. case DT_PRIVATE_DISTLIST:
  221. return true;
  222. case DT_MAILUSER:
  223. case DT_FORUM:
  224. case DT_AGENT:
  225. case DT_ORGANIZATION:
  226. case DT_REMOTE_MAILUSER:
  227. return false;
  228. default:
  229. ERROR_OUT(("Invalid DT in CGAL::IsItemBold"));
  230. return false;
  231. }
  232. return false;
  233. }
  234. HRESULT CGAL::_GetEmailNames( int* pnEmailNames, LPTSTR** ppszEmailNames, int iItem )
  235. {
  236. HRESULT hr = S_OK;
  237. *pnEmailNames = 1;
  238. *ppszEmailNames = new LPTSTR[1];
  239. (*ppszEmailNames)[0] = NULL;
  240. CGalEntry* pCurSel = _GetItemFromCache( iItem );
  241. if( pCurSel )
  242. {
  243. (*ppszEmailNames)[0] = PszAlloc( pCurSel->GetEMail() );
  244. }
  245. return hr;
  246. }
  247. /* virtual */ RAI * CGAL::GetAddrInfo(void)
  248. {
  249. RAI* pRai = NULL;
  250. int iItem = GetSelection();
  251. if (-1 != iItem)
  252. {
  253. HWND hwnd = GetHwnd();
  254. LPTSTR* pszPhoneNums = NULL;
  255. LPTSTR* pszEmailNames = NULL;
  256. int nPhoneNums = 0;
  257. int nEmailNames = 0;
  258. CGalEntry* pCurSel = _GetItemFromCache( iItem );
  259. if( g_fGkEnabled )
  260. {
  261. if( g_bGkPhoneNumberAddressing )
  262. {
  263. _GetPhoneNumbers( pCurSel->GetInstanceKey(), &nPhoneNums, &pszPhoneNums );
  264. }
  265. else
  266. {
  267. _GetEmailNames( &nEmailNames, &pszEmailNames, iItem );
  268. }
  269. }
  270. else
  271. { // This is regular call placement mode
  272. if( g_fGatewayEnabled )
  273. {
  274. _GetPhoneNumbers( pCurSel->GetInstanceKey(), &nPhoneNums, &pszPhoneNums );
  275. }
  276. nEmailNames = 1;
  277. pszEmailNames = new LPTSTR[1];
  278. pszEmailNames[0] = new TCHAR[CCHMAXSZ];
  279. GetSzAddress( pszEmailNames[0], CCHMAXSZ, iItem );
  280. }
  281. if( nPhoneNums || nEmailNames )
  282. {
  283. int nItems = nPhoneNums + nEmailNames;
  284. DWORD cbLen = sizeof(RAI) + sizeof(DWSTR)* nItems;
  285. pRai = reinterpret_cast<RAI*>(new BYTE[ cbLen ]);
  286. ZeroMemory(pRai, cbLen);
  287. pRai->cItems = nItems;
  288. int iCur = 0;
  289. lstrcpyn( pRai->szName, pCurSel->GetName(), CCHMAX(pRai->szName) );
  290. // First copy the e-mail names
  291. for( int i = 0; i < nEmailNames; i++ )
  292. {
  293. DWORD dwAddressType = g_fGkEnabled ? NM_ADDR_ALIAS_ID : NM_ADDR_ULS;
  294. pRai->rgDwStr[iCur].dw = dwAddressType;
  295. pRai->rgDwStr[iCur].psz = pszEmailNames[i];
  296. ++iCur;
  297. }
  298. delete [] pszEmailNames;
  299. // Copy the phone numbirs
  300. for( i = 0; i < nPhoneNums; i++ )
  301. {
  302. pRai->rgDwStr[iCur].dw = g_fGkEnabled ? NM_ADDR_ALIAS_E164 : NM_ADDR_H323_GATEWAY;
  303. pRai->rgDwStr[iCur].psz = pszPhoneNums[i];
  304. ++iCur;
  305. }
  306. delete [] pszPhoneNums;
  307. }
  308. }
  309. return pRai;
  310. }
  311. HRESULT CGAL::_GetPhoneNumbers( const SBinary& rEntryID, int* pcPhoneNumbers, LPTSTR** ppszPhoneNums )
  312. {
  313. HRESULT hr = S_OK;
  314. if( pcPhoneNumbers && ppszPhoneNums )
  315. {
  316. *pcPhoneNumbers = 0;
  317. *ppszPhoneNums = NULL;
  318. ULONG PhoneNumPropTags[] = {
  319. PR_BUSINESS_TELEPHONE_NUMBER,
  320. PR_HOME_TELEPHONE_NUMBER,
  321. PR_PRIMARY_TELEPHONE_NUMBER,
  322. PR_BUSINESS2_TELEPHONE_NUMBER,
  323. PR_CELLULAR_TELEPHONE_NUMBER,
  324. PR_RADIO_TELEPHONE_NUMBER,
  325. PR_CAR_TELEPHONE_NUMBER,
  326. PR_OTHER_TELEPHONE_NUMBER,
  327. PR_PAGER_TELEPHONE_NUMBER
  328. };
  329. BYTE* pb = new BYTE[ sizeof( SPropTagArray ) + sizeof( ULONG ) * ARRAY_ELEMENTS(PhoneNumPropTags) ];
  330. if( pb )
  331. {
  332. SPropTagArray* pta = reinterpret_cast<SPropTagArray*>(pb);
  333. pta->cValues = ARRAY_ELEMENTS(PhoneNumPropTags);
  334. for( UINT iCur = 0; iCur < pta->cValues; iCur++ )
  335. {
  336. pta->aulPropTag[iCur] = PhoneNumPropTags[iCur];
  337. }
  338. hr = m_pContentsTable->SetColumns(pta, TBL_BATCH);
  339. if (SUCCEEDED(hr))
  340. {
  341. if( SUCCEEDED( hr = _SetCursorTo( rEntryID ) ) )
  342. {
  343. LPSRowSet pRow;
  344. // Get the item from the GAL
  345. if ( SUCCEEDED ( hr = m_pContentsTable->QueryRows( 1, TBL_NOADVANCE, &pRow ) ) )
  346. {
  347. lst<LPTSTR> PhoneNums;
  348. // First we have to find out how many nums there are
  349. for( UINT iCur = 0; iCur < pRow->aRow->cValues; ++iCur )
  350. {
  351. if( LOWORD( pRow->aRow->lpProps[iCur].ulPropTag ) != PT_ERROR )
  352. {
  353. TCHAR szExtractedAddress[CCHMAXSZ];
  354. DWORD dwAddrType = g_fGkEnabled ? NM_ADDR_ALIAS_E164 : NM_ADDR_H323_GATEWAY;
  355. ExtractAddress( dwAddrType,
  356. #ifdef UNICODE
  357. pRow->aRow->lpProps[iCur].Value.lpszW,
  358. #else
  359. pRow->aRow->lpProps[iCur].Value.lpszA,
  360. #endif // UNICODE
  361. szExtractedAddress,
  362. CCHMAX(szExtractedAddress)
  363. );
  364. if( IsValidAddress( dwAddrType, szExtractedAddress ) )
  365. {
  366. ++(*pcPhoneNumbers);
  367. PhoneNums.push_back(PszAlloc(
  368. #ifdef UNICODE
  369. pRow->aRow->lpProps[iCur].Value.lpszW
  370. #else
  371. pRow->aRow->lpProps[iCur].Value.lpszA
  372. #endif // UNICODE
  373. )
  374. );
  375. }
  376. }
  377. }
  378. *ppszPhoneNums = new LPTSTR[ PhoneNums.size() ];
  379. if( *ppszPhoneNums )
  380. {
  381. lst<LPTSTR>::iterator I = PhoneNums.begin();
  382. int iCur = 0;
  383. while( I != PhoneNums.end() )
  384. {
  385. *ppszPhoneNums[iCur] = *I;
  386. ++iCur, ++I;
  387. }
  388. }
  389. else
  390. {
  391. hr = E_OUTOFMEMORY;
  392. }
  393. lpfnFreeProws( pRow );
  394. }
  395. }
  396. else
  397. {
  398. hr = E_OUTOFMEMORY;
  399. }
  400. }
  401. delete [] pb;
  402. }
  403. else
  404. {
  405. hr = E_OUTOFMEMORY;
  406. }
  407. }
  408. else
  409. {
  410. hr = E_POINTER;
  411. }
  412. return hr;
  413. }
  414. /* virtual */ void CGAL::OnListCacheHint( int indexFrom, int indexTo ) {
  415. if( !_IsLoggedOn() )
  416. {
  417. return;
  418. }
  419. // TRACE_OUT(("OnListCacheHint( %d, %d )", indexFrom, indexTo ));
  420. }
  421. /* virtual */ VOID CGAL::CmdProperties( void ) {
  422. int iItem = GetSelection();
  423. if (-1 == iItem) {
  424. return;
  425. }
  426. HRESULT hr;
  427. HWND hwnd = GetHwnd();
  428. CGalEntry* pCurSel = _GetItemFromCache( iItem );
  429. const SBinary& rEntryID = pCurSel->GetEntryID();
  430. ULONG ulFlags = DIALOG_MODAL;
  431. #ifdef UNICODE
  432. ulFlags |= MAPI_UNICODE;
  433. #endif // UNICODE
  434. hr = m_pAddrBook->Details( reinterpret_cast< LPULONG >( &hwnd ),
  435. NULL,
  436. NULL,
  437. rEntryID.cb,
  438. reinterpret_cast< LPENTRYID >( rEntryID.lpb ),
  439. NULL,
  440. NULL,
  441. NULL,
  442. ulFlags
  443. );
  444. }
  445. // This is called when the Global Address List item is selected from the
  446. // combo box in the call dialog
  447. /* virtual */ VOID CGAL::ShowItems(HWND hwnd)
  448. {
  449. CALV::SetHeader(hwnd, IDS_ADDRESS);
  450. ListView_SetItemCount(hwnd, 0);
  451. m_hWndListView = hwnd;
  452. if(SUCCEEDED(m_hrGALError))
  453. {
  454. TCHAR szPhoneNumber[CCHMAXSZ];
  455. if( FLoadString(IDS_PHONENUM, szPhoneNumber, CCHMAX(szPhoneNumber)) )
  456. {
  457. LV_COLUMN lvc;
  458. ClearStruct(&lvc);
  459. lvc.mask = LVCF_TEXT | LVCF_SUBITEM;
  460. lvc.pszText = szPhoneNumber;
  461. lvc.iSubItem = 2;
  462. ListView_InsertColumn(hwnd, IDI_DLGCALL_PHONENUM, &lvc);
  463. }
  464. m_MaxCacheSize = ListView_GetCountPerPage(hwnd) * NUM_LISTVIEW_PAGES_IN_CACHE;
  465. if (m_MaxCacheSize < m_MaxJumpSize)
  466. {
  467. // The cache has to be at least as big as the jump size
  468. m_MaxCacheSize = m_MaxJumpSize * 2;
  469. }
  470. if (!_IsLoggedOn())
  471. {
  472. _AsyncLogOntoGAL();
  473. }
  474. else
  475. {
  476. _sInitListViewAndGalColumns(hwnd);
  477. }
  478. }
  479. }
  480. /* C L E A R I T E M S */
  481. /*-------------------------------------------------------------------------
  482. %%Function: ClearItems
  483. -------------------------------------------------------------------------*/
  484. VOID CGAL::ClearItems(void)
  485. {
  486. CALV::ClearItems();
  487. if( IsWindow(m_hWndListView) )
  488. {
  489. ListView_DeleteColumn(m_hWndListView, IDI_DLGCALL_PHONENUM);
  490. }
  491. else
  492. {
  493. WARNING_OUT(("m_hWndListView is not valid in CGAL::ClearItems"));
  494. }
  495. }
  496. /* _ S I N I T L I S T V I E W A N D G A L C O L U M N S */
  497. /*-------------------------------------------------------------------------
  498. %%Function: _sInitListViewAndGalColumns
  499. -------------------------------------------------------------------------*/
  500. HRESULT CGAL::_sInitListViewAndGalColumns(HWND hwnd)
  501. {
  502. // Set the GAL columns before we let the listview try to get the data
  503. struct SPropTagArray_sptCols {
  504. ULONG cValues;
  505. ULONG aulPropTag[ NUM_PROPS ];
  506. } sptCols;
  507. sptCols.cValues = NUM_PROPS;
  508. sptCols.aulPropTag[ NAME_PROP_INDEX ] = PR_DISPLAY_NAME;
  509. sptCols.aulPropTag[ ACCOUNT_PROP_INDEX ] = PR_ACCOUNT;
  510. sptCols.aulPropTag[ INSTANCEKEY_PROP_INDEX ] = PR_INSTANCE_KEY;
  511. sptCols.aulPropTag[ ENTRYID_PROP_INDEX ] = PR_ENTRYID;
  512. sptCols.aulPropTag[ DISPLAY_TYPE_INDEX ] = PR_DISPLAY_TYPE;
  513. sptCols.aulPropTag[ BUSINESS_PHONE_NUM_PROP_INDEX ] = PR_BUSINESS_TELEPHONE_NUMBER;
  514. HRESULT hr = m_pContentsTable->SetColumns((LPSPropTagArray) &sptCols, TBL_BATCH);
  515. if (SUCCEEDED(hr))
  516. {
  517. // Get the row count so we can initialize the OWNER DATA ListView
  518. hr = m_pContentsTable->GetRowCount(0, &m_nRows);
  519. if (SUCCEEDED(hr))
  520. {
  521. // Set the list view size to the number of entries in the GAL
  522. ListView_SetItemCount(hwnd, m_nRows);
  523. }
  524. }
  525. return hr;
  526. }
  527. /* _ A S Y N C L O G O N T O G A L */
  528. /*-------------------------------------------------------------------------
  529. %%Function: _AsyncLogOntoGAL
  530. -------------------------------------------------------------------------*/
  531. HRESULT CGAL::_AsyncLogOntoGAL(void)
  532. {
  533. if ((AsyncLogonState_Idle != m_AsyncLogonState) ||
  534. (NULL != m_hAsyncLogOntoGalThread))
  535. {
  536. return S_FALSE;
  537. }
  538. m_AsyncLogonState = AsyncLogonState_LoggingOn;
  539. ASSERT(NULL == m_hEventEndAsyncThread);
  540. m_hEventEndAsyncThread = CreateEvent(NULL, TRUE, FALSE, NULL);
  541. DWORD dwThID;
  542. TRACE_OUT(("Creating AsyncLogOntoGal Thread"));
  543. m_hAsyncLogOntoGalThread = CreateThread(NULL, 0, _sAsyncLogOntoGalThreadfn,
  544. static_cast< LPVOID >(GetHwnd()), 0, &dwThID);
  545. if (NULL == m_hAsyncLogOntoGalThread)
  546. {
  547. m_AsyncLogonState = AsyncLogonState_Idle;
  548. return HRESULT_FROM_WIN32(GetLastError());
  549. }
  550. return S_OK;
  551. }
  552. /* static */ DWORD CALLBACK CGAL::_sAsyncLogOntoGalThreadfn(LPVOID pv)
  553. {
  554. SetBusyCursor(TRUE);
  555. HRESULT hr = _sAsyncLogOntoGal();
  556. SetBusyCursor(FALSE);
  557. if (S_OK == hr)
  558. {
  559. TRACE_OUT(("in _AsyncLogOntoGalThreadfn: Calling _InitListViewAndGalColumns"));
  560. _sInitListViewAndGalColumns((HWND) pv);
  561. // This keeps the thread around until we're done
  562. WaitForSingleObject(m_hEventEndAsyncThread, INFINITE);
  563. }
  564. // Clean up in the same thread
  565. hr = _sAsyncLogoffGal();
  566. return (DWORD) hr;
  567. }
  568. /* static */ HRESULT CGAL::_sAsyncLogOntoGal(void)
  569. {
  570. ULONG cbeid = 0L;
  571. LPENTRYID lpeid = NULL;
  572. HRESULT hr = S_OK;
  573. ULONG ulObjType;
  574. MAPIINIT_0 mi = { MAPI_INIT_VERSION, MAPI_MULTITHREAD_NOTIFICATIONS };
  575. TRACE_OUT(("in _AsyncLogOntoGalThreadfn: Calling MAPIInitialize"));
  576. hr = lpfnMAPIInitialize(&mi);
  577. if (FAILED(hr))
  578. return hr;
  579. TRACE_OUT(("in _AsyncLogOntoGalThreadfn: Calling MAPILogonEx"));
  580. IMAPISession* pMapiSession;
  581. hr = lpfnMAPILogonEx( NULL,
  582. NULL,
  583. NULL,
  584. MAPI_EXTENDED | MAPI_USE_DEFAULT,
  585. &pMapiSession );
  586. if (FAILED(hr))
  587. return hr;
  588. // Open the main address book
  589. TRACE_OUT(("in _AsyncLogOntoGalThreadfn: Calling OpenAddressBook"));
  590. ASSERT(NULL == m_pAddrBook);
  591. hr = pMapiSession->OpenAddressBook(NULL, NULL, AB_NO_DIALOG, &m_pAddrBook);
  592. pMapiSession->Release();
  593. pMapiSession = NULL;
  594. if (FAILED(hr))
  595. return hr;
  596. TRACE_OUT(("in _AsyncLogOntoGalThreadfn: Calling HrFindExchangeGlobalAddressList "));
  597. hr = HrFindExchangeGlobalAddressList(m_pAddrBook, &cbeid, &lpeid);
  598. if (FAILED(hr))
  599. return hr;
  600. TRACE_OUT(("in _AsyncLogOntoGalThreadfn: Calling OpenEntry"));
  601. ASSERT(NULL == m_pGAL);
  602. hr = m_pAddrBook->OpenEntry(cbeid, lpeid, NULL, MAPI_BEST_ACCESS,
  603. &ulObjType, reinterpret_cast< IUnknown** >( &m_pGAL));
  604. if (FAILED(hr))
  605. return hr;
  606. if (ulObjType != MAPI_ABCONT)
  607. return GAL_E_GAL_NOT_FOUND;
  608. TRACE_OUT(("in _AsyncLogOntoGalThreadfn: Calling GetContentsTable"));
  609. ASSERT(NULL == m_pContentsTable);
  610. hr = m_pGAL->GetContentsTable(0L, &m_pContentsTable);
  611. if (FAILED(hr))
  612. return hr;
  613. m_AsyncLogonState = AsyncLogonState_LoggedOn;
  614. return hr;
  615. }
  616. /* static */ HRESULT CGAL::_sAsyncLogoffGal(void)
  617. {
  618. // Free and release all the stuff that we hold onto
  619. TRACE_OUT(("in _AsyncLogOntoGalThreadfn: Releasing MAPI Interfaces"));
  620. if (NULL != m_pContentsTable)
  621. {
  622. m_pContentsTable->Release();
  623. m_pContentsTable = NULL;
  624. }
  625. if (NULL != m_pAddrBook)
  626. {
  627. m_pAddrBook->Release();
  628. m_pAddrBook = NULL;
  629. }
  630. if (NULL != m_pGAL)
  631. {
  632. m_pGAL->Release();
  633. m_pGAL = NULL;
  634. }
  635. WARNING_OUT(("in _AsyncLogOntoGalThreadfn: Calling lpfnMAPIUninitialize"));
  636. lpfnMAPIUninitialize();
  637. m_AsyncLogonState = AsyncLogonState_Idle;
  638. return S_OK;
  639. }
  640. HRESULT CGAL::_SetCursorTo( const CGalEntry& rEntry ) {
  641. return _SetCursorTo( rEntry.GetInstanceKey() );
  642. }
  643. HRESULT CGAL::_SetCursorTo( LPCTSTR szPartialMatch ) {
  644. // Find the row that matches the partial String based on the DISPLAY_NAME;
  645. SRestriction Restriction;
  646. SPropValue spv;
  647. Restriction.rt = RES_PROPERTY;
  648. Restriction.res.resProperty.relop = RELOP_GE;
  649. Restriction.res.resProperty.lpProp = &spv;
  650. Restriction.res.resProperty.ulPropTag = PR_DISPLAY_NAME;
  651. spv.ulPropTag = PR_DISPLAY_NAME;
  652. #ifdef UNICODE
  653. spv.Value.lpszW = const_cast< LPTSTR >( szPartialMatch );
  654. #else
  655. spv.Value.lpszA = const_cast< LPTSTR >( szPartialMatch );
  656. #endif // UNICODE
  657. // Find the first row that is lexographically greater than or equal to the search string
  658. HRESULT hr = m_pContentsTable->FindRow( &Restriction, BOOKMARK_BEGINNING, 0 );
  659. if( FAILED( hr ) ) {
  660. if( MAPI_E_NOT_FOUND == hr ) {
  661. // This is not really an error, because we handle it from the calling
  662. // function. That is, we don't have to set m_hrGALError here...
  663. return MAPI_E_NOT_FOUND;
  664. }
  665. m_hrGALError = GAL_E_QUERYROWS_FAILED;
  666. return GAL_E_QUERYROWS_FAILED;
  667. }
  668. return S_OK;
  669. }
  670. HRESULT CGAL::_SetCursorTo( const SBinary& rInstanceKey ) {
  671. HRESULT hr;
  672. // there is an exchange reg key, we have to get the user's data from the GAL
  673. SRestriction Restriction;
  674. SPropValue spv;
  675. // Search for the user using the instance key data that is in the CGalEntry for the currently
  676. // selected list box item
  677. Restriction.rt = RES_PROPERTY;
  678. Restriction.res.resProperty.relop = RELOP_EQ;
  679. Restriction.res.resProperty.ulPropTag = PR_INSTANCE_KEY;
  680. Restriction.res.resProperty.lpProp = &spv;
  681. spv.ulPropTag = PR_INSTANCE_KEY;
  682. // Get the INSTANCE_KEY from the cache
  683. spv.Value.bin.cb = rInstanceKey.cb;
  684. spv.Value.bin.lpb = new byte[ spv.Value.bin.cb ];
  685. ASSERT( spv.Value.bin.cb );
  686. memcpy( spv.Value.bin.lpb, rInstanceKey.lpb, spv.Value.bin.cb );
  687. // find the user in the table...
  688. hr = m_pContentsTable->FindRow( &Restriction, BOOKMARK_BEGINNING, 0 );
  689. if( FAILED( hr ) ) {
  690. m_hrGALError = GAL_E_FINDROW_FAILED;
  691. delete [] ( spv.Value.bin.lpb );
  692. return GAL_E_FINDROW_FAILED;
  693. }
  694. delete [] ( spv.Value.bin.lpb );
  695. return S_OK;
  696. }
  697. bool CGAL::_GetSzAddressFromExchangeServer( int iItem, LPTSTR psz, int cchMax ) {
  698. HRESULT hr;
  699. // In the registry, there may be a key that says what the MAPI attribute
  700. // is in which the users' ILS server is stored in the GAL... If the
  701. // reg key exists, we have to get the property from the GAL
  702. DWORD dwAttr = _GetExchangeAttribute( );
  703. bool bExtensionFound = ( NM_INVALID_MAPI_PROPERTY != dwAttr );
  704. // Re-create the table so that it includes the MAPI property tag found in the EXCHANGE REG ATTRUBITE
  705. SizedSPropTagArray( 3, sptColsExtensionFound ) = { 3, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PROP_TAG( PT_TSTRING, dwAttr) };
  706. SizedSPropTagArray( 2, sptColsExtensionNotFound ) = { 2, PR_EMAIL_ADDRESS, PR_ADDRTYPE };
  707. const int EmailPropertyIndex = 0;
  708. const int EmailAddressTypePropertyIndex = 1;
  709. const int ExtensionPropertyIndex = 2;
  710. if( bExtensionFound ) {
  711. if(FAILED(hr = m_pContentsTable->SetColumns( ( LPSPropTagArray ) &sptColsExtensionFound, TBL_BATCH ) ) ) {
  712. m_hrGALError = GAL_E_SETCOLUMNS_FAILED;
  713. return false;
  714. }
  715. }
  716. else {
  717. if(FAILED(hr = m_pContentsTable->SetColumns( ( LPSPropTagArray ) &sptColsExtensionNotFound, TBL_BATCH ) ) ) {
  718. m_hrGALError = GAL_E_SETCOLUMNS_FAILED;
  719. return false;
  720. }
  721. }
  722. if( FAILED( hr = _SetCursorTo( *_GetItemFromCache( iItem ) ) ) ) {
  723. return false;
  724. }
  725. LPSRowSet pRow;
  726. // Get the item from the GAL
  727. if ( SUCCEEDED ( hr = m_pContentsTable->QueryRows( 1, TBL_NOADVANCE, &pRow ) ) ) {
  728. if( bExtensionFound ) {
  729. // Copy the extension data from the entry if it is there
  730. if( LOWORD( pRow->aRow->lpProps[ ExtensionPropertyIndex ].ulPropTag ) != PT_ERROR ) {
  731. TRACE_OUT(("Using custom Exchange data for address"));
  732. _CopyPropertyString( psz, pRow->aRow->lpProps[ ExtensionPropertyIndex ], cchMax );
  733. lpfnFreeProws( pRow );
  734. return true;
  735. }
  736. }
  737. // If the extension was not found in the reg, or if there was no extension data.
  738. // use the e-mail address if it is SMTP type...
  739. if( LOWORD( pRow->aRow->lpProps[ EmailAddressTypePropertyIndex ].ulPropTag ) != PT_ERROR ) {
  740. // Check to see if the address type is SMTP
  741. #ifdef UNICODE
  742. TRACE_OUT(("Email address %s:%s", pRow->aRow->lpProps[ EmailAddressTypePropertyIndex ].Value.lpszW, pRow->aRow->lpProps[ EmailPropertyIndex ].Value.lpszW ));
  743. if( !lstrcmp( msc_szSMTPADDRESSNAME, pRow->aRow->lpProps[ EmailAddressTypePropertyIndex ].Value.lpszW ) ) {
  744. #else
  745. TRACE_OUT(("Email address %s:%s", pRow->aRow->lpProps[ EmailAddressTypePropertyIndex ].Value.lpszA, pRow->aRow->lpProps[ EmailPropertyIndex ].Value.lpszA ));
  746. if( !lstrcmp( msc_szSMTPADDRESSNAME, pRow->aRow->lpProps[ EmailAddressTypePropertyIndex ].Value.lpszA ) ) {
  747. #endif // UNICODE
  748. TRACE_OUT(("Using SMTP E-mail as address"));
  749. if( LOWORD( pRow->aRow->lpProps[ EmailPropertyIndex ].ulPropTag ) != PT_ERROR ) {
  750. FGetDefaultServer( psz, cchMax - 1 );
  751. int ServerPrefixLen = lstrlen( psz );
  752. psz[ ServerPrefixLen ] = TEXT( '/' );
  753. ++ServerPrefixLen;
  754. ASSERT( ServerPrefixLen < cchMax );
  755. _CopyPropertyString( psz + ServerPrefixLen, pRow->aRow->lpProps[ EmailPropertyIndex ], cchMax - ServerPrefixLen );
  756. lpfnFreeProws( pRow );
  757. return true;
  758. }
  759. }
  760. }
  761. lpfnFreeProws( pRow );
  762. }
  763. else {
  764. m_hrGALError = GAL_E_QUERYROWS_FAILED;
  765. return false;
  766. }
  767. // This means that we did not find the data on the server
  768. return false;
  769. }
  770. void CGAL::_CopyPropertyString( LPTSTR psz, SPropValue& rProp, int cchMax ) {
  771. #ifdef UNICODE
  772. lstrcpyn( psz, rProp.Value.lpszW, cchMax );
  773. #else
  774. lstrcpyn( psz, rProp.Value.lpszA, cchMax );
  775. #endif // UNICODE
  776. }
  777. // When the user selects CALL, we have to
  778. // Create an address for them to callto://
  779. BOOL CGAL::GetSzAddress(LPTSTR psz, int cchMax, int iItem)
  780. {
  781. // try and get the data from the exchange server as per the spec...
  782. if (_GetSzAddressFromExchangeServer(iItem, psz, cchMax))
  783. {
  784. TRACE_OUT(("CGAL::GetSzAddress() returning address [%s]", psz));
  785. return TRUE;
  786. }
  787. // If the data is not on the server, we are going to create the address in the format
  788. // <default_server>/<PR_ACCOUNT string>
  789. if (!FGetDefaultServer(psz, cchMax - 1))
  790. return FALSE;
  791. // Because the syntax is callto:<servername>/<username>
  792. // we have to put in the forward-slash
  793. int cch = lstrlen(psz);
  794. psz[cch++] = '/';
  795. psz += cch;
  796. cchMax -= cch;
  797. // There was no data on the server for us, so we will just use the PR_ACCOUNT data that we have cached
  798. return CALV::GetSzData(psz, cchMax, iItem, IDI_DLGCALL_ADDRESS);
  799. }
  800. // When the user types in a search string ( partial match string ) in the edit box
  801. // above the ListView, we want to show them the entries starting with the given string
  802. ULONG CGAL::OnListFindItem( LPCTSTR szPartialMatchingString ) {
  803. if( !_IsLoggedOn() )
  804. {
  805. return 0;
  806. }
  807. // If we have such an item cached, return the index to it
  808. int index;
  809. if( -1 != ( index = _FindItemInCache( szPartialMatchingString ) ) ) {
  810. return index;
  811. }
  812. // if the edit box is empty ( NULL string ), then we know to return item 0
  813. if( szPartialMatchingString[ 0 ] == '\0' ) {
  814. return 0;
  815. }
  816. HRESULT hr;
  817. if( FAILED( hr = _SetCursorTo( szPartialMatchingString ) ) ) {
  818. if( MAPI_E_NOT_FOUND == hr ) {
  819. return m_nRows - 1;
  820. }
  821. return 0;
  822. }
  823. // We have to find the row number of the cursor where the partial match is...
  824. ULONG ulRow, ulPositionNumerator, ulPositionDenominator;
  825. m_pContentsTable->QueryPosition( &ulRow, &ulPositionNumerator, &ulPositionDenominator );
  826. if( ulRow == 0xFFFFFFFF ) {
  827. // If QueryPosition is unable to determine the ROW, it will return the row based on the
  828. // fraction ulPositionNumerator/ulPositionDenominator
  829. ulRow = MulDiv( m_nRows, ulPositionNumerator, ulPositionDenominator );
  830. }
  831. // Kill the cache, becasue we are jumping to a new block of data
  832. // We do this because the _FindItemInCache call above failed to
  833. // return the desired item...
  834. _ResetCache();
  835. m_IndexOfFirstItemInCache = ulRow;
  836. m_IndexOfLastItemInCache = ulRow - 1;
  837. // Jump back a few, so we can cache some entries before the one we are looking for
  838. long lJumped;
  839. hr = m_pContentsTable->SeekRow( BOOKMARK_CURRENT, -( m_nBlockSize / 2 ), &lJumped );
  840. if( FAILED( hr ) ) {
  841. m_hrGALError = GAL_E_SEEKROW_FAILED;
  842. return 0;
  843. }
  844. // We hawe to change the sign of lJumped because we are jumping backwards
  845. lJumped *= -1;
  846. // Set the begin bookmark
  847. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfFirstItemInCache );
  848. ASSERT( SUCCEEDED( hr ) );
  849. m_bBeginningBookmarkIsValid = true;
  850. // Read in a block of rows
  851. LPSRowSet pRow = NULL;
  852. hr = m_pContentsTable->QueryRows( m_nBlockSize, 0, &pRow );
  853. if( FAILED( hr ) ) {
  854. m_hrGALError = GAL_E_QUERYROWS_FAILED;
  855. return 0;
  856. }
  857. // For each item in the block
  858. // This should always be the case,
  859. // but we vant to make sure that we have enough rows to get to the
  860. // item that we are looking for...
  861. ASSERT( pRow->cRows >= static_cast< ULONG >( lJumped ) );
  862. for( ULONG i = 0; i < pRow->cRows; i++ ) {
  863. CGalEntry* pEntry;
  864. if( FAILED( _MakeGalEntry( pRow->aRow[ i ], &pEntry ) ) ) {
  865. lpfnFreeProws( pRow );
  866. return 0;
  867. }
  868. if( 0 == lJumped ) {
  869. ulRow = m_IndexOfLastItemInCache + 1;
  870. }
  871. --lJumped;
  872. m_EntryCache.push_back( pEntry );
  873. m_IndexOfLastItemInCache++;
  874. }
  875. lpfnFreeProws( pRow );
  876. // Set the end bookmark
  877. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfItemAfterLastItemInCache );
  878. if( FAILED( hr ) ) {
  879. m_hrGALError = GAL_E_CREATEBOOKMARK_FAILED;
  880. return 0;
  881. }
  882. m_bEndBookmarkIsValid = true;
  883. VERIFYCACHE
  884. return ulRow;
  885. }
  886. // This is called by the ListView Notification handler. Because the ListView is OWNERDATA
  887. // it has to ask us every time it needs the string data for the columns...
  888. void CGAL::OnListGetColumn1Data( int iItemIndex, int cchTextMax, LPTSTR szBuf ) {
  889. if( !_IsLoggedOn() )
  890. {
  891. lstrcpyn( szBuf, g_cszEmpty, cchTextMax );
  892. }
  893. else
  894. {
  895. LPCTSTR pszName = _GetEntry( iItemIndex )->GetName();
  896. if( NULL == pszName )
  897. {
  898. pszName = g_cszEmpty;
  899. }
  900. lstrcpyn( szBuf, pszName, cchTextMax );
  901. }
  902. }
  903. // This is called by the ListView Notification handler. Because the ListView is OWNERDATA
  904. // it has to ask us every time it needs the string data for the columns...
  905. void CGAL::OnListGetColumn2Data( int iItemIndex, int cchTextMax, LPTSTR szBuf ) {
  906. if( !_IsLoggedOn() )
  907. {
  908. lstrcpyn( szBuf, g_cszEmpty, cchTextMax );
  909. }
  910. else {
  911. LPCTSTR pszEMail = _GetEntry( iItemIndex )->GetEMail();
  912. if( NULL == pszEMail )
  913. {
  914. pszEMail = g_cszEmpty;
  915. }
  916. lstrcpyn( szBuf, pszEMail, cchTextMax );
  917. }
  918. }
  919. // This is called by the ListView Notification handler. Because the ListView is OWNERDATA
  920. // it has to ask us every time it needs the string data for the columns...
  921. void CGAL::OnListGetColumn3Data( int iItemIndex, int cchTextMax, LPTSTR szBuf ) {
  922. if( !_IsLoggedOn() )
  923. {
  924. lstrcpyn( szBuf, g_cszEmpty, cchTextMax );
  925. }
  926. else {
  927. lstrcpyn( szBuf, g_cszEmpty, cchTextMax );
  928. LPCTSTR pszBusinessTelephone = _GetEntry( iItemIndex )->GetBusinessTelephone();
  929. if( NULL == pszBusinessTelephone )
  930. {
  931. pszBusinessTelephone = g_cszEmpty;
  932. }
  933. lstrcpyn( szBuf, pszBusinessTelephone, cchTextMax );
  934. }
  935. }
  936. // When the user types in a search string in the edit box, we first check to see if there is an
  937. // item in the cache that satisfys the partial search criteria
  938. int CGAL::_FindItemInCache( LPCTSTR szPartialMatchString ) {
  939. if( m_EntryCache.size() == 0 ) { return -1; }
  940. if( ( *( m_EntryCache.front() ) <= szPartialMatchString ) && ( *( m_EntryCache.back() ) >= szPartialMatchString ) ) {
  941. int index = m_IndexOfFirstItemInCache;
  942. lst< CGalEntry* >::iterator I = m_EntryCache.begin();
  943. while( ( *( *I ) ) < szPartialMatchString ) {
  944. ++I;
  945. ++index;
  946. }
  947. return index;
  948. }
  949. return -1;
  950. }
  951. // _GetEntry returns a reference to the desired entry. If the entry is in the cache, it retrieves it, and if
  952. // it is not in the cache, it loads it from the GAL and saves it in the cache
  953. CGAL::CGalEntry* CGAL::_GetEntry( int index )
  954. {
  955. CGalEntry* pRet = &msc_ErrorEntry_NoGAL;
  956. if (!_IsLoggedOn() || FAILED(m_hrGALError))
  957. {
  958. // rRet = msc_ErrorEntry_NoGAL;
  959. }
  960. // If the entry is in the cache, return it
  961. else if( ( index >= m_IndexOfFirstItemInCache ) && ( index <= m_IndexOfLastItemInCache ) ) {
  962. pRet = _GetItemFromCache( index );
  963. }
  964. else if( m_EntryCache.size() == 0 ) {
  965. // If the cache is empty, LongJump
  966. // Do a long jump to index, reset the cached data and return the item at index
  967. pRet = _LongJumpTo( index );
  968. }
  969. else if( ( index < m_IndexOfFirstItemInCache ) && ( ( m_IndexOfFirstItemInCache - index ) <= m_MaxJumpSize ) ) {
  970. // If index is less than the first index by less than m_MaxJumSize
  971. // Fill in the entries below the first index and return the item at _index_
  972. pRet = _GetEntriesAtBeginningOfList( index );
  973. }
  974. else if( ( index > m_IndexOfLastItemInCache ) && ( ( index - m_IndexOfLastItemInCache ) <= m_MaxJumpSize ) ) {
  975. // else if index is greater than the last index by less than m_MaxJumpSize
  976. // Fill in the entries above the last index and return the item at _index_
  977. pRet = _GetEntriesAtEndOfList( index );
  978. }
  979. else {
  980. // Do a long jump to index, reset the cached data and return the item at index
  981. pRet = _LongJumpTo( index );
  982. }
  983. return pRet;
  984. }
  985. // If the ListView needs an item that is far enough away from the current cache block to require a
  986. // new cache block, this function in called. The cache is destroyed and a new cache block is
  987. // created at the longjump item's index
  988. CGAL::CGalEntry* CGAL::_LongJumpTo( int index ) {
  989. HRESULT hr;
  990. // first we have to kill the cache and free the old bookmarks because they will no longer be valid...
  991. _ResetCache();
  992. // Seek approximately to the spot that we are looking for...
  993. int CacheIndex = index;
  994. int Offset = m_nBlockSize / 2;
  995. if( CacheIndex < Offset ) {
  996. CacheIndex = 0;
  997. }
  998. else {
  999. CacheIndex -= Offset;
  1000. }
  1001. hr = m_pContentsTable->SeekRowApprox( CacheIndex, m_nRows );
  1002. if( FAILED( hr ) ) {
  1003. m_hrGALError = GAL_E_SEEKROWAPPROX_FAILED;
  1004. return &msc_ErrorEntry_SeekRowApproxFailed;
  1005. }
  1006. m_IndexOfFirstItemInCache = CacheIndex;
  1007. m_IndexOfLastItemInCache = m_IndexOfFirstItemInCache - 1;
  1008. // Set the beginningBookmark
  1009. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfFirstItemInCache );
  1010. if( FAILED( hr ) ) {
  1011. m_hrGALError = GAL_E_CREATEBOOKMARK_FAILED;
  1012. return &msc_ErrorEntry_CreateBookmarkFailed;
  1013. }
  1014. m_bBeginningBookmarkIsValid = true;
  1015. lst< CGalEntry* >::iterator IRet = m_EntryCache.end();
  1016. // Get a block of rows
  1017. LPSRowSet pRow = NULL;
  1018. hr = m_pContentsTable->QueryRows( m_nBlockSize, 0, &pRow );
  1019. if( FAILED( hr ) ) {
  1020. m_hrGALError = GAL_E_QUERYROWS_FAILED;
  1021. return &msc_ErrorEntry_QueryRowsFailed;
  1022. }
  1023. // For each item in the block
  1024. for( ULONG i = 0; i < pRow->cRows; i++ ) {
  1025. CGalEntry* pEntry;
  1026. if( FAILED( _MakeGalEntry( pRow->aRow[ i ], &pEntry ) ) ) {
  1027. lpfnFreeProws( pRow );
  1028. return &msc_ErrorEntry_NoInstanceKeyFound;
  1029. }
  1030. m_EntryCache.push_back( pEntry );
  1031. // if the current item is equal to the first item in our list, we are done
  1032. m_IndexOfLastItemInCache++;
  1033. if( m_IndexOfLastItemInCache == index ) {
  1034. IRet = --( m_EntryCache.end() );
  1035. }
  1036. }
  1037. if( IRet == m_EntryCache.end() ) {
  1038. // There is a small chance that this could happen
  1039. // if there were problems on the server.
  1040. WARNING_OUT(("In CGAL::_LongJumpTo(...) QueryRows only returned %u items", pRow->cRows ));
  1041. WARNING_OUT(("\tm_IndexOfFirstItemInCache = %u, m_IndexOfLastItemInCache = %u, index = %u", m_IndexOfFirstItemInCache, m_IndexOfLastItemInCache, index ));
  1042. m_hrGALError = GAL_E_QUERYROWS_FAILED;
  1043. return &msc_ErrorEntry_QueryRowsFailed;
  1044. }
  1045. lpfnFreeProws( pRow );
  1046. ASSERT( ( m_IndexOfLastItemInCache - m_IndexOfFirstItemInCache ) == static_cast< int >( m_EntryCache.size() - 1 ) );
  1047. // Set the beginningBookmark
  1048. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfItemAfterLastItemInCache );
  1049. if( FAILED( hr ) ) {
  1050. m_hrGALError = GAL_E_CREATEBOOKMARK_FAILED;
  1051. return &msc_ErrorEntry_CreateBookmarkFailed;
  1052. }
  1053. m_bEndBookmarkIsValid = true;
  1054. VERIFYCACHE
  1055. return *IRet;
  1056. }
  1057. // If the user is scrolling backwards and comes to an index whose data is not in the cache, we hawe
  1058. // to get some entries at the beginning of the list... We will start at a position somewhat before the
  1059. // first item's index and keep getting items from the GAL until we have all the items up to the first item
  1060. // in the list. We continue to jump back a little and get items to the beginning of the list until we have
  1061. // cached the requested index. Because we have the item handy, we will return it
  1062. CGAL::CGalEntry* CGAL::_GetEntriesAtBeginningOfList( int index ) {
  1063. HRESULT hr;
  1064. // The beginning bookmark may not be valid, because the user may have been scrolling forward
  1065. // and because the cache is kept at a constant size, the item at the front bookmark may have
  1066. // been removed from the cache. If this is the case, we have to re-create the front bookmark
  1067. if( !m_bBeginningBookmarkIsValid ) {
  1068. if( _CreateBeginningBookmark() ) {
  1069. // This means that the listView needs to be update
  1070. ListView_RedrawItems( GetHwnd(), 0, m_nRows );
  1071. return &msc_ErrorEntry_FindRowFailed;
  1072. }
  1073. }
  1074. // Seek row to the beginning bookmark -m_nBlockSize items
  1075. long lJumped;
  1076. hr = m_pContentsTable->SeekRow( m_BookmarkOfFirstItemInCache, -m_nBlockSize, &lJumped );
  1077. if( FAILED( hr ) ) {
  1078. m_hrGALError = GAL_E_SEEKROW_FAILED;
  1079. return &msc_ErrorEntry_SeekRowFailed;
  1080. }
  1081. lJumped *= -1; // We have to change the sign on this number ( which will be negative )
  1082. ASSERT( SUCCEEDED( hr ) );
  1083. if( 0 == lJumped ) {
  1084. // We are at the beginning of the list
  1085. m_IndexOfLastItemInCache -= m_IndexOfFirstItemInCache;
  1086. m_IndexOfFirstItemInCache = 0;
  1087. }
  1088. else {
  1089. // Free the beginningBookmark
  1090. hr = m_pContentsTable->FreeBookmark( m_BookmarkOfFirstItemInCache );
  1091. if( FAILED( hr ) ) {
  1092. m_hrGALError = GAL_E_FREEBOOKMARK_FAILED;
  1093. return &msc_ErrorEntry_FreeBookmarkFailed;
  1094. }
  1095. // Set the beginningBookmark
  1096. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfFirstItemInCache );
  1097. if( FAILED( hr ) ) {
  1098. m_hrGALError = GAL_E_CREATEBOOKMARK_FAILED;
  1099. return &msc_ErrorEntry_CreateBookmarkFailed;
  1100. }
  1101. }
  1102. // QueryRow for lJumped items
  1103. lst< CGalEntry* >::iterator IInsertPos = m_EntryCache.begin();
  1104. // Get a block of rows
  1105. LPSRowSet pRow = NULL;
  1106. hr = m_pContentsTable->QueryRows( lJumped, 0, &pRow );
  1107. if( FAILED( hr ) ) {
  1108. m_hrGALError = GAL_E_QUERYROWS_FAILED;
  1109. return &msc_ErrorEntry_QueryRowsFailed;
  1110. }
  1111. // For each item in the block
  1112. for( ULONG i = 0; i < pRow->cRows; i++ ) {
  1113. CGalEntry* pEntry;
  1114. if( FAILED( _MakeGalEntry( pRow->aRow[ i ], &pEntry ) ) ) {
  1115. lpfnFreeProws( pRow );
  1116. return &msc_ErrorEntry_NoInstanceKeyFound;
  1117. }
  1118. // if the current item is equal to the first item in our list, we are done
  1119. --m_IndexOfFirstItemInCache;
  1120. m_EntryCache.insert( IInsertPos, pEntry );
  1121. }
  1122. VERIFYCACHE
  1123. lpfnFreeProws( pRow );
  1124. if( FAILED( _KillExcessItemsFromBackOfCache() ) ) {
  1125. // THis ist the only thing that can fail in _KillExcessItemsFromBackOfCache
  1126. return &msc_ErrorEntry_FreeBookmarkFailed;
  1127. }
  1128. ASSERT( ( m_IndexOfLastItemInCache - m_IndexOfFirstItemInCache ) == static_cast< int >( m_EntryCache.size() - 1 ) );
  1129. // return the item corresponding to the index
  1130. return _GetItemFromCache( index );
  1131. }
  1132. HRESULT CGAL::_KillExcessItemsFromBackOfCache( void ) {
  1133. // if the cache size is greater than m_MaxCacheSize
  1134. if( m_EntryCache.size() > static_cast< size_t >( m_MaxCacheSize ) ) {
  1135. // kill as many as we need to from the front of the list, fixing m_IndexOfFirstItemInCache
  1136. int NumItemsToKill = ( m_EntryCache.size() - m_MaxCacheSize );
  1137. while( NumItemsToKill-- ) {
  1138. delete m_EntryCache.back();
  1139. m_EntryCache.erase( --( m_EntryCache.end() ) );
  1140. --m_IndexOfLastItemInCache;
  1141. }
  1142. // Free the beginning bookmark
  1143. if( m_bEndBookmarkIsValid ) {
  1144. // flag the front bookmark as invalid
  1145. m_bEndBookmarkIsValid = false;
  1146. HRESULT hr = m_pContentsTable->FreeBookmark( m_BookmarkOfItemAfterLastItemInCache );
  1147. if( FAILED( hr ) ) {
  1148. m_hrGALError = GAL_E_FREEBOOKMARK_FAILED;
  1149. return m_hrGALError;
  1150. }
  1151. }
  1152. }
  1153. return S_OK;
  1154. }
  1155. // In certain circumstances _CreateBeginningBookmark will return TRUE to indicate that the listView needs to be updated...
  1156. bool CGAL::_CreateBeginningBookmark( void ) {
  1157. HRESULT hr;
  1158. bool bRet = false;
  1159. if( FAILED( hr = _SetCursorTo( *m_EntryCache.front() ) ) ) {
  1160. if( MAPI_E_NOT_FOUND == hr ) {
  1161. // The item is not in the table anymore. We have to
  1162. _LongJumpTo( m_IndexOfFirstItemInCache );
  1163. return true;
  1164. }
  1165. else {
  1166. m_hrGALError = GAL_E_FINDROW_FAILED;
  1167. return false;
  1168. }
  1169. }
  1170. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfFirstItemInCache );
  1171. m_bBeginningBookmarkIsValid = true;
  1172. if( FAILED( hr ) ) {
  1173. m_hrGALError = GAL_E_CREATEBOOKMARK_FAILED;
  1174. return false;
  1175. }
  1176. return false;
  1177. }
  1178. // ruturn true if the item at IEntry is the item requested at index
  1179. bool CGAL::_CreateEndBookmark( int index, lst< CGalEntry* >::iterator& IEntry ) {
  1180. HRESULT hr;
  1181. bool bRet = false;
  1182. IEntry = m_EntryCache.end();
  1183. hr = _SetCursorTo( *m_EntryCache.back() );
  1184. if( FAILED( hr ) ) {
  1185. }
  1186. if( FAILED( hr ) ) {
  1187. if( MAPI_E_NOT_FOUND == hr ) {
  1188. // This means that the listView needs to be update
  1189. ListView_RedrawItems( GetHwnd(), 0, m_nRows );
  1190. IEntry = m_EntryCache.end();
  1191. return true;
  1192. }
  1193. else {
  1194. m_hrGALError = GAL_E_FINDROW_FAILED;
  1195. return false;
  1196. }
  1197. }
  1198. // Get a block of entries
  1199. LPSRowSet pRow = NULL;
  1200. // Get a bunch of rows
  1201. hr = m_pContentsTable->QueryRows( m_nBlockSize, 0, &pRow );
  1202. if( FAILED( hr ) ) {
  1203. m_hrGALError = GAL_E_QUERYROWS_FAILED;
  1204. return false;
  1205. }
  1206. // If no entries are returned, this means that we have hit the end of the list
  1207. if( 0 == ( pRow->cRows ) ) {
  1208. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfItemAfterLastItemInCache );
  1209. if( FAILED( hr ) ) {
  1210. m_hrGALError = GAL_E_CREATEBOOKMARK_FAILED;
  1211. return true;
  1212. }
  1213. m_bEndBookmarkIsValid = true;
  1214. IEntry = --( m_EntryCache.end() );
  1215. return true;
  1216. }
  1217. // Verify that the first entry is the last item in our list
  1218. ASSERT( 0 == memcmp( pRow->aRow[ 0 ].lpProps[ INSTANCEKEY_PROP_INDEX ].Value.bin.lpb, m_EntryCache.back()->GetInstanceKey().lpb, pRow->aRow[ 0 ].lpProps[ INSTANCEKEY_PROP_INDEX ].Value.bin.cb ) );
  1219. // for each entry returned
  1220. for( ULONG i = 1; i < pRow->cRows; i++ ) {
  1221. CGalEntry* pEntry;
  1222. if( FAILED( _MakeGalEntry( pRow->aRow[ i ], &pEntry ) ) ) {
  1223. lpfnFreeProws( pRow );
  1224. return false;
  1225. }
  1226. // push it to the back of the entry list and increment m_IndexOfLastItemInCache
  1227. m_EntryCache.push_back( pEntry );
  1228. m_IndexOfLastItemInCache++;
  1229. if( m_IndexOfLastItemInCache == index ) {
  1230. bRet = true;
  1231. IEntry = --( m_EntryCache.end() );
  1232. }
  1233. }
  1234. lpfnFreeProws( pRow );
  1235. if( FAILED( _KillExcessItemsFromFrontOfCache() ) ) {
  1236. // This is the only thang that can fail in _KillExcessItemsFromFrontOfCache
  1237. return false;
  1238. }
  1239. ASSERT( ( m_IndexOfLastItemInCache - m_IndexOfFirstItemInCache ) == static_cast< int >( m_EntryCache.size() - 1 ) );
  1240. // Create a bookmark and store it in m_BookmarkOfItemAfterLastItemInCache
  1241. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfItemAfterLastItemInCache );
  1242. if( FAILED( hr ) ) {
  1243. m_hrGALError = GAL_E_CREATEBOOKMARK_FAILED;
  1244. return true;
  1245. }
  1246. m_bEndBookmarkIsValid = true;
  1247. return bRet;
  1248. }
  1249. // If the user is scrolling forwards and the ListView requests an item that is a little bit beyond the
  1250. // end of the cache, we have to get some more entries...
  1251. CGAL::CGalEntry* CGAL::_GetEntriesAtEndOfList( int index ) {
  1252. lst< CGalEntry* >::iterator IRet;
  1253. HRESULT hr;
  1254. // if m_bEndBookmarkIsValid
  1255. if( m_bEndBookmarkIsValid ) {
  1256. // SeekRow to m_BookmarkOfItemAfterLastItemInCache
  1257. hr = m_pContentsTable->SeekRow( m_BookmarkOfItemAfterLastItemInCache, 0, NULL );
  1258. if( FAILED( hr ) ) {
  1259. m_hrGALError = GAL_E_SEEKROW_FAILED;
  1260. return &msc_ErrorEntry_SeekRowFailed;
  1261. }
  1262. }
  1263. else {
  1264. // Set the end bookmark to the item after the last item in the cache
  1265. if( _CreateEndBookmark( index, IRet ) ) {
  1266. if( IRet != m_EntryCache.end() ) {
  1267. VERIFYCACHE
  1268. return *IRet;
  1269. }
  1270. // this means that the end item is no longer in the GAL table
  1271. // we have to update the list view
  1272. _LongJumpTo( index );
  1273. ListView_RedrawItems( GetHwnd(), 0, m_nRows );
  1274. return &msc_ErrorEntry_FindRowFailed;
  1275. }
  1276. }
  1277. if( index > m_IndexOfLastItemInCache ) {
  1278. // Get a block of entries
  1279. LPSRowSet pRow = NULL;
  1280. // Get a bunch of rows
  1281. hr = m_pContentsTable->QueryRows( m_nBlockSize, 0, &pRow );
  1282. if( FAILED( hr ) ) {
  1283. m_hrGALError = GAL_E_QUERYROWS_FAILED;
  1284. return &msc_ErrorEntry_QueryRowsFailed;
  1285. }
  1286. // If no entries are returned, this means that we have hit the end of the list
  1287. if( 0 == ( pRow->cRows ) ) {
  1288. return m_EntryCache.back();
  1289. }
  1290. // for each entry returned
  1291. for( ULONG i = 0; i < pRow->cRows; i++ ) {
  1292. CGalEntry* pEntry;
  1293. if( FAILED( _MakeGalEntry( pRow->aRow[ i ], &pEntry ) ) ) {
  1294. lpfnFreeProws( pRow );
  1295. return &msc_ErrorEntry_NoInstanceKeyFound;
  1296. }
  1297. // push it to the back of the entry list and increment m_IndexOfLastItemInCache
  1298. m_EntryCache.push_back( pEntry );
  1299. m_IndexOfLastItemInCache++;
  1300. // if m_IndexOfLastItemInCache == index, store the iterator for when we return the entry
  1301. if( index == m_IndexOfLastItemInCache ) {
  1302. IRet = --( m_EntryCache.end() );
  1303. }
  1304. }
  1305. lpfnFreeProws( pRow );
  1306. // Free m_BookmarkOfItemAfterLastItemInCache
  1307. hr = m_pContentsTable->FreeBookmark( m_BookmarkOfItemAfterLastItemInCache );
  1308. if( FAILED( hr ) ) {
  1309. m_hrGALError = GAL_E_FREEBOOKMARK_FAILED;
  1310. return &msc_ErrorEntry_FreeBookmarkFailed;
  1311. }
  1312. // Create a bookmark and store it in m_BookmarkOfItemAfterLastItemInCache
  1313. hr = m_pContentsTable->CreateBookmark( &m_BookmarkOfItemAfterLastItemInCache );
  1314. if( FAILED( hr ) ) {
  1315. m_hrGALError = GAL_E_CREATEBOOKMARK_FAILED;
  1316. return &msc_ErrorEntry_CreateBookmarkFailed;
  1317. }
  1318. ASSERT( ( m_IndexOfLastItemInCache - m_IndexOfFirstItemInCache ) == static_cast< int >( m_EntryCache.size() - 1 ) );
  1319. }
  1320. if( FAILED( _KillExcessItemsFromFrontOfCache() ) ) {
  1321. // This is the only thang that can fail in _KillExcessItemsFromFrontOfCache
  1322. return &msc_ErrorEntry_FreeBookmarkFailed;
  1323. }
  1324. VERIFYCACHE
  1325. // return the entry
  1326. return *IRet;
  1327. }
  1328. // The only thing that can fail is freebookmark, in which case GAL_E_FREEBOOKMARK_FAILED is returned
  1329. HRESULT CGAL::_KillExcessItemsFromFrontOfCache( void ) {
  1330. // if the cache size is greater than m_MaxCacheSize
  1331. if( m_EntryCache.size() > static_cast< size_t >( m_MaxCacheSize ) ) {
  1332. // kill as many as we need to from the front of the list, fixing m_IndexOfFirstItemInCache
  1333. int NumItemsToKill = ( m_EntryCache.size() - m_MaxCacheSize );
  1334. while( NumItemsToKill-- ) {
  1335. delete m_EntryCache.front();
  1336. m_EntryCache.erase( m_EntryCache.begin() );
  1337. ++m_IndexOfFirstItemInCache;
  1338. }
  1339. // flag the front bookmark as invalid
  1340. m_bBeginningBookmarkIsValid = false;
  1341. HRESULT hr = m_pContentsTable->FreeBookmark( m_BookmarkOfFirstItemInCache );
  1342. if( FAILED( hr ) ) {
  1343. m_hrGALError = GAL_E_FREEBOOKMARK_FAILED;
  1344. return GAL_E_FREEBOOKMARK_FAILED;
  1345. }
  1346. }
  1347. return S_OK;
  1348. }
  1349. // _GetItemInCache will return an entry from the cache
  1350. // the cache size should be set to a small enough number that
  1351. // the fact that we are using a linear search should not be a problem
  1352. // if we wanted to support a larger cache, another collection class other than a lst class
  1353. // would be used ( like a tree or a hash table )
  1354. CGAL::CGalEntry* CGAL::_GetItemFromCache( int index ) {
  1355. ASSERT( ( m_IndexOfLastItemInCache - m_IndexOfFirstItemInCache ) == static_cast< int >( m_EntryCache.size() - 1 ) );
  1356. lst< CGalEntry* >::iterator I = m_EntryCache.begin();
  1357. int i = m_IndexOfFirstItemInCache;
  1358. while( i != index ) {
  1359. ASSERT( I != m_EntryCache.end() );
  1360. ++i, ++I;
  1361. }
  1362. return *I;
  1363. }
  1364. // There may be a registry key that stores the mapi property that the user should
  1365. // use to find the ils server and username of people that the user calls with the GAL....
  1366. // If this reg key exists, the MAPI property will be queried when the user presses the CALL button
  1367. // in the dialog....
  1368. DWORD CGAL::_GetExchangeAttribute( void ) {
  1369. RegEntry re( msc_szNMPolRegKey, HKEY_CURRENT_USER );
  1370. return re.GetNumber( msc_szNMExchangeAtrValue, NM_INVALID_MAPI_PROPERTY );
  1371. }
  1372. void CGAL::_ResetCache( void ) {
  1373. HRESULT hr;
  1374. lst< CGalEntry* >::iterator I = m_EntryCache.begin();
  1375. while( I != m_EntryCache.end() ) {
  1376. delete ( *I );
  1377. I++;
  1378. }
  1379. m_EntryCache.erase( m_EntryCache.begin(), m_EntryCache.end() );
  1380. m_IndexOfFirstItemInCache = INVALID_CACHE_INDEX;
  1381. m_IndexOfLastItemInCache = INVALID_CACHE_INDEX - 1;
  1382. if( m_bBeginningBookmarkIsValid ) {
  1383. hr = m_pContentsTable->FreeBookmark( m_BookmarkOfFirstItemInCache );
  1384. if( FAILED( hr ) ) {
  1385. m_hrGALError = GAL_E_FREEBOOKMARK_FAILED;
  1386. return;
  1387. }
  1388. }
  1389. if( m_bEndBookmarkIsValid ) {
  1390. hr = m_pContentsTable->FreeBookmark( m_BookmarkOfItemAfterLastItemInCache );
  1391. if( FAILED( hr ) ) {
  1392. m_hrGALError = GAL_E_FREEBOOKMARK_FAILED;
  1393. return;
  1394. }
  1395. }
  1396. }
  1397. // Create a GAL Entry from a SRow structure returned by QueryRows
  1398. // The username and EMail name may be absent, this is not an error
  1399. // if the INSTANCE_KEY is missing, that would constitute an error
  1400. HRESULT CGAL::_MakeGalEntry( SRow& rRow, CGalEntry** ppEntry ) {
  1401. *ppEntry = NULL;
  1402. LPSPropValue lpProps = rRow.lpProps;
  1403. LPCTSTR szName;
  1404. if( LOWORD( lpProps[ NAME_PROP_INDEX ].ulPropTag ) != PT_ERROR ) {
  1405. #ifdef UNICODE
  1406. szName = lpProps[ NAME_PROP_INDEX ].Value.lpszW;
  1407. #else
  1408. szName = lpProps[ NAME_PROP_INDEX ].Value.lpszA;
  1409. #endif // UNICODE
  1410. }
  1411. else {
  1412. szName = msc_szNoDisplayName;
  1413. }
  1414. LPCTSTR szEMail;
  1415. if( LOWORD( lpProps[ ACCOUNT_PROP_INDEX ].ulPropTag ) != PT_ERROR ) {
  1416. #ifdef UNICODE
  1417. szEMail = lpProps[ ACCOUNT_PROP_INDEX ].Value.lpszW;
  1418. #else
  1419. szEMail = lpProps[ ACCOUNT_PROP_INDEX ].Value.lpszA;
  1420. #endif // UNICODE
  1421. }
  1422. else {
  1423. szEMail = msc_szNoEMailName;
  1424. }
  1425. // Get the instance key
  1426. if( LOWORD( lpProps[ INSTANCEKEY_PROP_INDEX ].ulPropTag ) == PT_ERROR ) {
  1427. m_hrGALError = GAL_E_NOINSTANCEKEY;
  1428. return m_hrGALError;
  1429. }
  1430. ASSERT( PR_INSTANCE_KEY == lpProps[ INSTANCEKEY_PROP_INDEX ].ulPropTag );
  1431. SBinary& rInstanceKey = lpProps[ INSTANCEKEY_PROP_INDEX ].Value.bin;
  1432. // Get the entryid
  1433. if( LOWORD( lpProps[ ENTRYID_PROP_INDEX ].ulPropTag ) == PT_ERROR ) {
  1434. m_hrGALError = GAL_E_NOENTRYID;
  1435. return m_hrGALError;
  1436. }
  1437. ASSERT( PR_ENTRYID == lpProps[ ENTRYID_PROP_INDEX ].ulPropTag );
  1438. SBinary& rEntryID = lpProps[ ENTRYID_PROP_INDEX ].Value.bin;
  1439. // Get the display Type
  1440. ULONG ulDisplayType = DT_MAILUSER;
  1441. if( LOWORD( lpProps[ DISPLAY_TYPE_INDEX ].ulPropTag ) != PT_ERROR ) {
  1442. ulDisplayType = lpProps[ DISPLAY_TYPE_INDEX ].Value.ul;
  1443. }
  1444. // Get the business telephone number
  1445. LPCTSTR szBusinessTelephoneNum;
  1446. if( LOWORD( lpProps[ BUSINESS_PHONE_NUM_PROP_INDEX ].ulPropTag ) != PT_ERROR ) {
  1447. #ifdef UNICODE
  1448. szBusinessTelephoneNum = lpProps[ BUSINESS_PHONE_NUM_PROP_INDEX ].Value.lpszW;
  1449. #else
  1450. szBusinessTelephoneNum = lpProps[ BUSINESS_PHONE_NUM_PROP_INDEX ].Value.lpszA;
  1451. #endif // UNICODE
  1452. }
  1453. else {
  1454. szBusinessTelephoneNum = msc_szNoBusinessTelephoneNum;
  1455. }
  1456. *ppEntry = new CGalEntry( szName, szEMail, rInstanceKey, rEntryID, ulDisplayType, szBusinessTelephoneNum );
  1457. return S_OK;
  1458. }
  1459. ////////////////////////////////////////////////////////////////////////////////////
  1460. ////////////////////////////////////////////////////////////////////////////////////
  1461. ////////////////////////////////////////////////////////////////////////////////////
  1462. CGAL::CGalEntry::CGalEntry( void )
  1463. : m_szName( NULL ),
  1464. m_szEMail( NULL ),
  1465. m_ulDisplayType( DT_MAILUSER ),
  1466. m_szBusinessTelephoneNum(NULL)
  1467. {
  1468. m_EntryID.cb = 0;
  1469. m_EntryID.lpb = NULL;
  1470. m_InstanceKey.cb = 0;
  1471. m_InstanceKey.lpb = NULL;
  1472. }
  1473. CGAL::CGalEntry::CGalEntry( const CGalEntry& r )
  1474. : m_szName( NULL ),
  1475. m_szEMail( NULL ),
  1476. m_ulDisplayType( DT_MAILUSER ),
  1477. m_szBusinessTelephoneNum(NULL)
  1478. {
  1479. m_EntryID.cb = 0;
  1480. m_EntryID.lpb = NULL;
  1481. m_InstanceKey.cb = 0;
  1482. m_InstanceKey.lpb = NULL;
  1483. *this = r;
  1484. }
  1485. CGAL::CGalEntry::CGalEntry( LPCTSTR szName, LPCTSTR szEMail, SBinary& rInstanceKey, SBinary& rEntryID, ULONG ulDisplayType, LPCTSTR szBusinessTelephoneNum )
  1486. : m_ulDisplayType( ulDisplayType )
  1487. {
  1488. m_EntryID.cb = rEntryID.cb;
  1489. m_InstanceKey.cb = rInstanceKey.cb;
  1490. if( m_EntryID.cb ) {
  1491. m_EntryID.lpb = new BYTE[ m_EntryID.cb ];
  1492. memcpy( m_EntryID.lpb, rEntryID.lpb, m_EntryID.cb );
  1493. }
  1494. if( m_InstanceKey.cb ) {
  1495. m_InstanceKey.lpb = new BYTE[ m_InstanceKey.cb ];
  1496. memcpy( m_InstanceKey.lpb, rInstanceKey.lpb, m_InstanceKey.cb );
  1497. }
  1498. m_szName = PszAlloc( szName );
  1499. m_szEMail = PszAlloc( szEMail );
  1500. m_szBusinessTelephoneNum = PszAlloc( szBusinessTelephoneNum );
  1501. }
  1502. CGAL::CGalEntry::CGalEntry( LPCTSTR szName, LPCTSTR szEMail )
  1503. : m_ulDisplayType( DT_MAILUSER ),
  1504. m_szBusinessTelephoneNum(NULL)
  1505. {
  1506. m_EntryID.cb = 0;
  1507. m_EntryID.lpb = NULL;
  1508. m_InstanceKey.cb = 0;
  1509. m_InstanceKey.lpb = NULL;
  1510. m_szName = PszAlloc( szName );
  1511. m_szEMail = PszAlloc( szEMail );
  1512. }
  1513. CGAL::CGalEntry::~CGalEntry( void ) {
  1514. delete [] m_szName;
  1515. delete [] m_szEMail;
  1516. delete [] m_EntryID.lpb;
  1517. delete [] m_InstanceKey.lpb;
  1518. delete [] m_szBusinessTelephoneNum;
  1519. }
  1520. CGAL::CGalEntry& CGAL::CGalEntry::operator=( const CGalEntry& r ) {
  1521. if( this != &r ) {
  1522. m_ulDisplayType = r.m_ulDisplayType;
  1523. delete [] m_EntryID.lpb;
  1524. m_EntryID.lpb = NULL;
  1525. delete [] m_InstanceKey.lpb;
  1526. m_InstanceKey.lpb = NULL;
  1527. delete [] m_szName;
  1528. delete [] m_szEMail;
  1529. delete [] m_szBusinessTelephoneNum;
  1530. m_szName = NULL;
  1531. m_szEMail = NULL;
  1532. m_szBusinessTelephoneNum = NULL;
  1533. m_EntryID.cb = r.m_EntryID.cb;
  1534. if( m_EntryID.cb ) {
  1535. m_EntryID.lpb = new BYTE[ m_EntryID.cb ];
  1536. memcpy( m_EntryID.lpb, r.m_EntryID.lpb, m_EntryID.cb );
  1537. }
  1538. m_InstanceKey.cb = r.m_InstanceKey.cb;
  1539. if( m_InstanceKey.cb ) {
  1540. m_InstanceKey.lpb = new BYTE[ m_InstanceKey.cb ];
  1541. memcpy( m_InstanceKey.lpb, r.m_InstanceKey.lpb, m_InstanceKey.cb );
  1542. }
  1543. m_szName = PszAlloc( r.m_szName );
  1544. m_szEMail = PszAlloc( r.m_szEMail );
  1545. m_szBusinessTelephoneNum = PszAlloc( r.m_szBusinessTelephoneNum );
  1546. }
  1547. return *this;
  1548. }
  1549. bool CGAL::CGalEntry::operator==( const CGalEntry& r ) const {
  1550. return ( ( m_InstanceKey.cb == r.m_InstanceKey.cb ) && ( 0 == memcmp( &m_InstanceKey.cb, &r.m_InstanceKey.cb, m_InstanceKey.cb ) ) );
  1551. }
  1552. bool CGAL::CGalEntry::operator>=( LPCTSTR sz ) const {
  1553. return ( 0 <= lstrcmpi( m_szName, sz ) );
  1554. }
  1555. bool CGAL::CGalEntry::operator<( LPCTSTR sz ) const {
  1556. return ( 0 > lstrcmpi( m_szName, sz ) );
  1557. }
  1558. bool CGAL::CGalEntry::operator<=( LPCTSTR sz ) const {
  1559. return ( 0 >= lstrcmpi( m_szName, sz ) );
  1560. }
  1561. ////////////////////////////////////////////////////////////////////////////////////
  1562. ////////////////////////////////////////////////////////////////////////////////////
  1563. ////////////////////////////////////////////////////////////////////////////////////
  1564. /////////////////////////////////////////////////////////////////////////
  1565. // Testin functions... These are pretty streight-forward....
  1566. #if TESTING_CGAL
  1567. void CGAL::_VerifyCache( void ) {
  1568. #if 0
  1569. if( !IS_ZONE_ENABLED( ghZoneApi, ZONE_GALVERIFICATION_FLAG ) ) { return; }
  1570. HRESULT hr;
  1571. hr = _SetCursorTo( *m_EntryCache.front() );
  1572. lst< CGalEntry* >::iterator I = m_EntryCache.begin();
  1573. while( m_EntryCache.end() != I ) {
  1574. LPSRowSet pRow;
  1575. hr = m_pContentsTable->QueryRows ( 50, 0, &pRow );
  1576. ASSERT( SUCCEEDED( hr ) );
  1577. for( ULONG i = 0; i < pRow->cRows; i++ ) {
  1578. CGalEntry* pEntry;
  1579. _MakeGalEntry( pRow->aRow[ i ], &pEntry );
  1580. if( ( **I ) != ( *pEntry ) ) {
  1581. ULONG Count;
  1582. hr = m_pContentsTable->GetRowCount( 0, &Count );
  1583. ASSERT( SUCCEEDED( hr ) );
  1584. ASSERT( 0 );
  1585. lpfnFreeProws( pRow );
  1586. delete pEntry;
  1587. return;
  1588. }
  1589. delete pEntry;
  1590. I++;
  1591. if( m_EntryCache.end() == I ) { break; }
  1592. }
  1593. lpfnFreeProws( pRow );
  1594. }
  1595. #endif
  1596. }
  1597. char* _MakeRandomString( void ) {
  1598. static char sz[ 200 ];
  1599. int len = ( rand() % 6 ) + 1;
  1600. sz[ len ] = '\0';
  1601. for( int i = len - 1; len >= 0; len-- ) {
  1602. sz[ len ] = ( rand() % 26 ) + 'a';
  1603. }
  1604. return sz;
  1605. }
  1606. void CGAL::_Test( void ) {
  1607. int e = 7557;
  1608. _GetEntry( e );
  1609. for( int o = 0; o < 10; o++ ) {
  1610. _GetEntry( e - o );
  1611. }
  1612. for( int i = 0; i < 500; i++ ) {
  1613. int nEntry = rand() % ( m_nRows - 1 );
  1614. _GetEntry( nEntry );
  1615. if( rand() % 2 ) {
  1616. // Slide for a while
  1617. int j, NewIndex;
  1618. int nSlide = rand() % 100;
  1619. if( rand() % 2 ) {
  1620. // Slide Up for a while
  1621. for( j = 0; j < nSlide; j++ ) {
  1622. NewIndex = j + nEntry;
  1623. if( ( NewIndex >= 0 ) && ( NewIndex < static_cast< int >( m_nRows ) ) ) {
  1624. _GetEntry( NewIndex );
  1625. }
  1626. }
  1627. }
  1628. else {
  1629. // Slide Down for a while
  1630. for( j = 0; j < nSlide; j++ ) {
  1631. NewIndex = nEntry - j;
  1632. if( ( NewIndex >= 0 ) && ( NewIndex < static_cast< int >( m_nRows ) ) ) {
  1633. _GetEntry( NewIndex );
  1634. }
  1635. }
  1636. }
  1637. }
  1638. }
  1639. TRACE_OUT(( "The first test is successful!" ));
  1640. _ResetCache();
  1641. for( i = 0; i < 500; i++ ) {
  1642. int nEntry = OnListFindItem( _MakeRandomString() );
  1643. if( rand() % 2 ) {
  1644. // Slide for a while
  1645. int j, NewIndex;
  1646. int nSlide = rand() % 100;
  1647. if( rand() % 2 ) {
  1648. // Slide Up for a while
  1649. for( j = 0; j < nSlide; j++ ) {
  1650. NewIndex = j + nEntry;
  1651. if( ( NewIndex >= 0 ) && ( NewIndex < static_cast< int >( m_nRows ) ) ) {
  1652. _GetEntry( NewIndex );
  1653. }
  1654. }
  1655. }
  1656. else {
  1657. // Slide Down for a while
  1658. for( j = 0; j < nSlide; j++ ) {
  1659. NewIndex = nEntry - j;
  1660. if( ( NewIndex >= 0 ) && ( NewIndex < static_cast< int >( m_nRows ) ) ) {
  1661. _GetEntry( NewIndex );
  1662. }
  1663. }
  1664. }
  1665. }
  1666. }
  1667. TRACE_OUT(( "The second test is successful!" ));
  1668. }
  1669. #endif // #if TESTING_CGAL