Source code of Windows XP (NT5)
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.

932 lines
26 KiB

  1. /***********************************************************************
  2. *
  3. * ABCTBL1.C
  4. *
  5. * Contents Table
  6. *
  7. * The contents table associated with this provider. It's file based.
  8. * The format of the .FAB file is in ABP.H.
  9. *
  10. * This implementation of IMAPITable is retrieved by calling the
  11. * GetContentsTable() method of the Microsoft At Work Fax ABP's single
  12. * directory (see abcont.c).
  13. *
  14. * There are a few interesting features in this implementation. First
  15. * it's implemented on a flat file (.FAB). Second, it actually supports
  16. * an implementation of ANR (Ambiguous Name Resolution). And lastly, it
  17. * supports FindRow (limited to prefix searching on display names).
  18. *
  19. * This is the minimum set of restrictions that the provider MUST support.
  20. * It MUST have an ANR implementation and support prefix (downward) searching
  21. * on whatever the contents table is sorted on (in this case PR_DISPLAY_NAME).
  22. *
  23. * The browsing of a flat file is done a record at a time. It's never
  24. * completely read into memory. It only reads records from the file when
  25. * it absolutely has to (like in QueryRows). The advantage to this is
  26. * a low memory footprint and the ability to browse rather large files
  27. * with decent performance.
  28. */
  29. /*
  30. * ANR is also supported in this implementation. In the code will often
  31. * be 'if' statements making two different code paths between the restricted
  32. * and unrestricted version. The restrictions rely on a couple of
  33. * bit arrays. Each bit corresponds (in order) to the records in the .FAB
  34. * file. One bit array, rgChecked, says whether or not a record in the
  35. * .FAB file has actually been compared to the restriction. It's initially
  36. * set to all 0s - which means that none of the records have been checked.
  37. * The second bit array, rgMatched, says whether or not the particular
  38. * record matches the current restriction. This array is initialized to all
  39. * 1s - which says that all the records in the .FAB file match the current
  40. * restriction. The reason for this is for the fraction returned in
  41. * QueryPosition--By assuming that all the rows match and only updating
  42. * the array when something doesn't match, the fraction returned in
  43. * QueryPosition will get larger and has the effect of making the thumb-bar
  44. * on a listbox implemented on top of this table to scroll down as you go
  45. * down the list.
  46. *
  47. * As a row is about to be read it is checked to see if it's been compared
  48. * to the current restriction. If it has then to determine whether or not
  49. * to actually read the record and build the row we just look at the matched
  50. * bit array. If it doesn't match we go on to the next record and check
  51. * again. If it does match we read the record and build the row.
  52. *
  53. * Only if it hasn't been checked do we actually go and read the row and
  54. * compare it to the current restriction, setting the rgChecked and
  55. * rgMatched bits accordingly.
  56. *
  57. * In this implementation the ANR comparison rules are as follows:
  58. * (See FNameMatch for the actual code to do this)
  59. *
  60. * A 'word' is defined as any substring separated by separators.
  61. * The separators are defined as ',', ' ', and '-'.
  62. * A restriction is said to match a display name iff all the words
  63. * in the restriction are all prefixes of words in the display name.
  64. *
  65. * For example, a restriction of "b d" will match:
  66. * "Barney Donovan"
  67. * "Donovan, Barney"
  68. * "Danielle Blumenthal"
  69. * But will not match:
  70. * "Darby Opal"
  71. * "Ben Masters"
  72. *
  73. * Other providers can do whatever matching they want with the ANR
  74. * restriction. For example, soundex or additional field comparisons (like
  75. * email addresses). A good (and fast) implementation will differentiate
  76. * your provider from others in a positive way.
  77. *
  78. * FindRow's implementation effectively handles prefix searching on display
  79. * names (PR_DISPLAY_NAME). It does this by binary searching the .FAB file.
  80. * The only tricky part is knowing when to stop. Basically you want to stop
  81. * on the first name in the list that matches the restriction. It's easy to
  82. * do linearly, but a little more trick with a binary search. This
  83. * implementation is a reasonable example.
  84. *
  85. *
  86. * This table has only the following columns:
  87. */
  88. #include "faxab.h"
  89. const SizedSPropTagArray(cvalvtMax, tagaivtabcColSet) =
  90. {
  91. cvalvtMax,
  92. {
  93. PR_DISPLAY_NAME_A,
  94. PR_ENTRYID,
  95. PR_ADDRTYPE,
  96. PR_EMAIL_ADDRESS_A,
  97. PR_OBJECT_TYPE,
  98. PR_DISPLAY_TYPE,
  99. PR_INSTANCE_KEY
  100. }
  101. };
  102. const LPSPropTagArray ptagaivtabcColSet = (LPSPropTagArray) &tagaivtabcColSet;
  103. /*
  104. *
  105. * The following routines are implemented in the files abctbl2.c and abctbl3.c:
  106. *
  107. *
  108. * IVTABC_Release
  109. * IVTABC_GetStatus
  110. * IVTABC_SetColumns
  111. * IVTABC_QueryColumns
  112. * IVTABC_GetRowCount
  113. * IVTABC_SeekRow
  114. * IVTABC_SeekRowApprox
  115. * IVTABC_QueryPosition
  116. * IVTABC_FindRow
  117. * IVTABC_Restrict
  118. * IVTABC_CreateBookmark
  119. * IVTABC_FreeBookmark
  120. * IVTABC_SortTable
  121. * IVTABC_QuerySortOrder
  122. * IVTABC_QueryRows
  123. * IVTABC_Abort
  124. * IVTABC_ExpandRow
  125. * IVTABC_CollapseRow
  126. * IVTABC_WaitForCompletion
  127. * IVTABC_GetCollapseState
  128. * IVTABC_SetCollapseState
  129. *
  130. *
  131. * This file (abctbl1.c) has all the utility functions used throughout this
  132. * objects methods. They are the following:
  133. *
  134. * HrNewIVTAbc()
  135. * HrValidateEntry()
  136. * FChecked()
  137. * FMatched()
  138. * FNameMatch()
  139. * HrOpenFile()
  140. * fIVTAbcIdleRoutine()
  141. * FreeANRBitmaps()
  142. *
  143. *
  144. * When Who What
  145. * -------- ------------------ ---------------------------------------
  146. * 1.1.94 MAPI Original source from MAPI sample AB Provider
  147. * 3.7.94 Yoram Yaacovi Update to MAPI build 154
  148. * 3.11.94 Yoram Yaacovi Update to use Fax AB include files
  149. * 8.1.94 Yoram Yaacovi Update to MAPI 304 + file name change to abctbl1.c
  150. * 11.7.94 Yoram Yaacovi Update to MAPI 318 (added PR_INSTANCE_KEY)
  151. *
  152. * Copyright 1992, 1993, 1994 Microsoft Corporation. All Rights Reserved.
  153. *
  154. ***********************************************************************/
  155. /*
  156. * vtbl filled in here.
  157. */
  158. const IVTABC_Vtbl vtblIVTABC =
  159. {
  160. IVTABC_QueryInterface,
  161. (IVTABC_AddRef_METHOD *) ROOT_AddRef,
  162. IVTABC_Release,
  163. (IVTABC_GetLastError_METHOD *) ROOT_GetLastError,
  164. IVTABC_Advise,
  165. IVTABC_Unadvise,
  166. IVTABC_GetStatus,
  167. IVTABC_SetColumns,
  168. IVTABC_QueryColumns,
  169. IVTABC_GetRowCount,
  170. IVTABC_SeekRow,
  171. IVTABC_SeekRowApprox,
  172. IVTABC_QueryPosition,
  173. IVTABC_FindRow,
  174. IVTABC_Restrict,
  175. IVTABC_CreateBookmark,
  176. IVTABC_FreeBookmark,
  177. IVTABC_SortTable,
  178. IVTABC_QuerySortOrder,
  179. IVTABC_QueryRows,
  180. IVTABC_Abort,
  181. IVTABC_ExpandRow,
  182. IVTABC_CollapseRow,
  183. IVTABC_WaitForCompletion,
  184. IVTABC_GetCollapseState,
  185. IVTABC_SetCollapseState
  186. };
  187. /* Idle function */
  188. FNIDLE fIVTAbcIdleRoutine;
  189. #define IVTABC_IDLE_INTERVAL 300L
  190. #define IVTABC_IDLE_PRIORITY -2
  191. #define IVTABC_IDLE_FLAGS FIROINTERVAL
  192. /*************************************************************************
  193. *
  194. - NewIVTAbc
  195. -
  196. * Creates a new IMAPITable on the contents of a .FAB file.
  197. *
  198. *
  199. */
  200. HRESULT
  201. HrNewIVTAbc( LPMAPITABLE * lppIVTAbc,
  202. LPABLOGON lpABLogon,
  203. LPABCONT lpABC,
  204. HINSTANCE hLibrary,
  205. LPALLOCATEBUFFER lpAllocBuff,
  206. LPALLOCATEMORE lpAllocMore,
  207. LPFREEBUFFER lpFreeBuff,
  208. LPMALLOC lpMalloc
  209. )
  210. {
  211. LPIVTABC lpIVTAbc = NULL;
  212. SCODE scode;
  213. HRESULT hResult = hrSuccess;
  214. ULONG ulBK, ulSize, ulSizeHigh;
  215. /*
  216. * Allocate space for the IVTABC structure
  217. */
  218. scode = lpAllocBuff(SIZEOF(IVTABC), (LPVOID *) &lpIVTAbc);
  219. if (FAILED(scode))
  220. {
  221. hResult = ResultFromScode(scode);
  222. goto err;
  223. }
  224. lpIVTAbc->lpVtbl = &vtblIVTABC;
  225. lpIVTAbc->lcInit = 1;
  226. lpIVTAbc->hResult = hrSuccess;
  227. lpIVTAbc->idsLastError = 0;
  228. lpIVTAbc->hLibrary = hLibrary;
  229. lpIVTAbc->lpAllocBuff = lpAllocBuff;
  230. lpIVTAbc->lpAllocMore = lpAllocMore;
  231. lpIVTAbc->lpFreeBuff = lpFreeBuff;
  232. lpIVTAbc->lpMalloc = lpMalloc;
  233. lpIVTAbc->lpABLogon = lpABLogon;
  234. lpIVTAbc->lpszFileName = NULL;
  235. hResult = HrLpszGetCurrentFileName(lpABLogon, &(lpIVTAbc->lpszFileName));
  236. if (HR_FAILED(hResult))
  237. {
  238. goto err;
  239. }
  240. lpIVTAbc->lpPTAColSet = (LPSPropTagArray) &tagaivtabcColSet;
  241. /*
  242. * Open the .FAB file associated with this logged in session.
  243. * Currently it's opened with FILE_SHARED_READ. This has an
  244. * unpleasant side-effect of not being able to modify the .FAB
  245. * file while this table is active.
  246. *
  247. * It should be OF_SHARE_DENY_NONE. I don't have the code to
  248. * handle this in this version.
  249. */
  250. if ((lpIVTAbc->hFile = CreateFile(
  251. lpIVTAbc->lpszFileName,
  252. GENERIC_READ,
  253. FILE_SHARE_READ|FILE_SHARE_WRITE,
  254. NULL,
  255. OPEN_EXISTING,
  256. FILE_ATTRIBUTE_NORMAL,
  257. NULL)) == INVALID_HANDLE_VALUE)
  258. {
  259. /*
  260. * The file didn't open...
  261. */
  262. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  263. SetErrorIDS(lpABC, hResult, IDS_CANT_OPEN_FAB);
  264. goto err;
  265. }
  266. /*
  267. * Get the time and date stamp
  268. *
  269. */
  270. (void)GetFileTime(lpIVTAbc->hFile, NULL, NULL, &(lpIVTAbc->filetime));
  271. /* Get the size of the file */
  272. if ((ulSize = GetFileSize(lpIVTAbc->hFile, &ulSizeHigh)) == 0xFFFFFFFF)
  273. {
  274. /*
  275. * MAYBE I have an error
  276. */
  277. if (GetLastError() != NO_ERROR)
  278. {
  279. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  280. SetErrorIDS(lpABC, hResult, IDS_FAB_FILE_ATTRIB);
  281. goto err;
  282. }
  283. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  284. SetErrorIDS(lpABC, hResult, IDS_FAB_TOO_LARGE);
  285. goto err;
  286. }
  287. /*
  288. * Actual number of valid positions
  289. */
  290. lpIVTAbc->ulMaxPos = (ulSize / SIZEOF(ABCREC));
  291. /*
  292. * Check to see if it's an exact multiple of sizeof(ABCREC)
  293. */
  294. if (lpIVTAbc->ulMaxPos * SIZEOF(ABCREC) != ulSize)
  295. {
  296. /*
  297. * Nope.
  298. */
  299. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  300. SetErrorIDS(lpABC, hResult, IDS_FAB_CORRUPT);
  301. goto err;
  302. }
  303. lpIVTAbc->ulPosition = 0;
  304. /* Setup bookmarks */
  305. for (ulBK = 0; ulBK < MAX_BOOKMARKS; lpIVTAbc->rglpABCBK[ulBK++] = NULL);
  306. /*
  307. * Restriction stuff
  308. */
  309. lpIVTAbc->rgChecked = NULL;
  310. lpIVTAbc->rgMatched = NULL;
  311. lpIVTAbc->lpszPartialName = NULL;
  312. /*
  313. * Registered notification stuff
  314. */
  315. lpIVTAbc->cAdvise = 0;
  316. lpIVTAbc->parglpAdvise = NULL;
  317. lpIVTAbc->ulConnectMic = (ULONG) lpIVTAbc & 0xffffff;
  318. InitializeCriticalSection(&lpIVTAbc->cs);
  319. /*
  320. * Register the idle function
  321. */
  322. lpIVTAbc->ftg = FtgRegisterIdleRoutine(
  323. fIVTAbcIdleRoutine,
  324. lpIVTAbc,
  325. IVTABC_IDLE_PRIORITY,
  326. IVTABC_IDLE_INTERVAL,
  327. IVTABC_IDLE_FLAGS);
  328. InitializeCriticalSection(&lpIVTAbc->cs);
  329. *lppIVTAbc = (LPVOID) lpIVTAbc;
  330. out:
  331. DebugTraceResult(HrNewIVTAbc, hResult);
  332. return hResult;
  333. err:
  334. if (lpIVTAbc)
  335. {
  336. if (lpIVTAbc->hFile != INVALID_HANDLE_VALUE)
  337. {
  338. /*
  339. * Must've opened the file
  340. */
  341. CloseHandle(lpIVTAbc->hFile);
  342. }
  343. /* Free the current .FAB file name */
  344. lpFreeBuff(lpIVTAbc->lpszFileName);
  345. /* Free the mem */
  346. lpFreeBuff( lpIVTAbc );
  347. }
  348. goto out;
  349. }
  350. /*************************************************************************
  351. *
  352. - HrValidateEntry
  353. -
  354. *
  355. * Used in restricted lists
  356. * Checks to see if a given entry at a particular location matches
  357. * the active restriction. It always modifies rgChecked, and may
  358. * modify rgMatched.
  359. */
  360. HRESULT
  361. HrValidateEntry(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  362. {
  363. ABCREC abcrec;
  364. ULONG cbRead;
  365. HRESULT hResult;
  366. /*
  367. * Open the file
  368. */
  369. hResult = HrOpenFile(lpIVTAbc);
  370. if (HR_FAILED(hResult))
  371. {
  372. DebugTraceResult(HrValidateEntry, hResult);
  373. return hResult;
  374. }
  375. /*
  376. * Set the file position to lNewPos
  377. */
  378. (void) SetFilePointer( lpIVTAbc->hFile,
  379. ulNewPos * SIZEOF(ABCREC),
  380. NULL,
  381. FILE_BEGIN
  382. );
  383. /*
  384. * Read in the record from the file
  385. */
  386. if ( !ReadFile( lpIVTAbc->hFile, (LPVOID) &abcrec,
  387. SIZEOF(ABCREC), &cbRead, NULL)
  388. )
  389. {
  390. DebugTraceSc(HrValidateEntry, MAPI_E_DISK_ERROR);
  391. return ResultFromScode(MAPI_E_DISK_ERROR);
  392. }
  393. /* Second check */
  394. if (cbRead != SIZEOF(ABCREC))
  395. {
  396. DebugTraceSc(HrValidateEntry, MAPI_E_DISK_ERROR);
  397. return ResultFromScode(MAPI_E_DISK_ERROR);
  398. }
  399. /*
  400. * Always set the Checked flag
  401. */
  402. lpIVTAbc->rgChecked[ulNewPos / 8] |= (((UCHAR)0x80) >> (ulNewPos % 8));
  403. /*
  404. * See if the rgchDisplayName matches the restriction
  405. */
  406. if (!FNameMatch(lpIVTAbc, abcrec.rgchDisplayName))
  407. {
  408. /*
  409. * Apparently not. Reset the Matched flag
  410. */
  411. lpIVTAbc->ulRstrDenom--;
  412. lpIVTAbc->rgMatched[ulNewPos / 8] &= ~(((UCHAR)0x80) >> (ulNewPos % 8));
  413. }
  414. return hrSuccess;
  415. }
  416. /*************************************************************************
  417. *
  418. - FChecked
  419. -
  420. *
  421. * Returns whether or not an entry has ever been checked
  422. * Just looks at the bit in the rgChecked array that corresponds with
  423. * lNewPos
  424. *
  425. */
  426. BOOL
  427. FChecked(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  428. {
  429. ULONG ulT = (ULONG) (lpIVTAbc->rgChecked[ulNewPos / 8] & (((UCHAR)0x80) >> (ulNewPos % 8)));
  430. return (BOOL) !!ulT;
  431. }
  432. /*************************************************************************
  433. *
  434. - FMatched
  435. -
  436. *
  437. * Returns whether or not an entry has been matched
  438. * Just checks the bit in the rgMatched array corresponding with
  439. * lNewPos
  440. *
  441. */
  442. BOOL
  443. FMatched(LPIVTABC lpIVTAbc, ULONG ulNewPos)
  444. {
  445. ULONG ulT = (lpIVTAbc->rgMatched[ulNewPos / 8] & (((UCHAR)0x80) >> (ulNewPos % 8)));
  446. return (BOOL) !!ulT;
  447. }
  448. /*************************************************************************
  449. *
  450. - FNameMatch
  451. -
  452. * Tries to match the rgchDisplayName with the partial name of the
  453. * restriction.
  454. * It tries to prefix match each partial name component (i.e. word) with
  455. * each rgchDisplayName name component (i.e. word). Only if all of them
  456. * match (from the partial name) does this return TRUE.
  457. */
  458. BOOL
  459. FCharInString(LPTSTR lpsz, TCHAR ch);
  460. BOOL
  461. FNameMatch(LPIVTABC lpIVTAbc, LPTSTR rgchDisplayName)
  462. {
  463. LPTSTR szANRSep = TEXT(", -");
  464. LPTSTR szANR = lpIVTAbc->lpszPartialName;
  465. LPTSTR pchEndSzANR = szANR + lstrlen(szANR);
  466. ULONG cchANRName;
  467. ULONG cchFullNameName;
  468. LPTSTR szFullNameT;
  469. LPTSTR szT;
  470. /* If someone tries to match more than an iwMOMax-part name, the function
  471. * will return fFalse. But then if someone is trying to match a name
  472. * with iwMOMax parts, chances are they weren't going to get it right
  473. * anyway....
  474. */
  475. #define iwMOMax 50
  476. WORD rgwMO[iwMOMax];
  477. int iwMOMac = 0;
  478. while (TRUE)
  479. {
  480. /* Find the end of the partial name we're pointing at */
  481. szT = szANR;
  482. while (!FCharInString(szANRSep, *szT))
  483. ++szT;
  484. cchANRName = szT - szANR;
  485. /* Check if it matches any name in the full name */
  486. szFullNameT = rgchDisplayName;
  487. while (TRUE)
  488. {
  489. szT = szFullNameT;
  490. /* Find the length of the name in the full name */
  491. /* we're checking against. */
  492. while (!FCharInString(szANRSep, *szT))
  493. ++szT;
  494. cchFullNameName = szT - szFullNameT;
  495. if (cchANRName <= cchFullNameName &&
  496. CompareString( lcidUser,
  497. NORM_IGNORECASE,
  498. szANR,
  499. (int) cchANRName,
  500. szFullNameT,
  501. (int) cchANRName) == 2 )
  502. {
  503. int iwMO;
  504. for (iwMO = 0; iwMO < iwMOMac; iwMO++)
  505. if (rgwMO[iwMO] == (WORD) (szFullNameT - rgchDisplayName))
  506. break;
  507. /* We found the partial name so check the next ANR part */
  508. if (iwMO == iwMOMac)
  509. {
  510. if (iwMOMac == iwMOMax - 1)
  511. {
  512. /* If some user wants to match an iwMOMax part
  513. * name, chances are it wasn't going to match
  514. * anyway...
  515. */
  516. return FALSE;
  517. }
  518. rgwMO[iwMOMac++] = szFullNameT - rgchDisplayName;
  519. break;
  520. }
  521. }
  522. /* We didn't find the partial name this time around, so
  523. * try to check the next name in the full name.
  524. */
  525. szFullNameT += cchFullNameName;
  526. while (*szFullNameT && FCharInString(szANRSep, *szFullNameT))
  527. ++szFullNameT;
  528. if (*szFullNameT == TEXT('\0'))
  529. return FALSE; /* We never found the partial name. */
  530. }
  531. /* We found the partial name, so check the next ANR part */
  532. szANR += cchANRName;
  533. while (*szANR && FCharInString(szANRSep, *szANR))
  534. ++szANR;
  535. if (*szANR == TEXT('\0'))
  536. return TRUE; /* No more ANR to check, so we found `em all */
  537. }
  538. /* Not reached (we hope...) */
  539. return FALSE;
  540. }
  541. /*
  542. - HrOpenFile
  543. -
  544. * Opens the .FAB file associated with the table and
  545. * checks whether the .FAB file has changed.
  546. * If it has changed, the table bookmarks and ANR bitmaps
  547. * are updated and everyone on the advise list is notified.
  548. */
  549. HRESULT
  550. HrOpenFile(LPIVTABC lpIVTAbc)
  551. {
  552. HRESULT hResult = hrSuccess;
  553. FILETIME filetime;
  554. ULONG ulSize, ulSizeHigh, ulMaxPos;
  555. LPTSTR lpszFileName = NULL;
  556. /*
  557. * If file is not open, open it
  558. */
  559. if (lpIVTAbc->hFile == INVALID_HANDLE_VALUE)
  560. {
  561. if (!FEqualFABFiles(lpIVTAbc->lpABLogon, lpIVTAbc->lpszFileName))
  562. {
  563. /*
  564. * Get the new file name
  565. */
  566. hResult = HrLpszGetCurrentFileName(lpIVTAbc->lpABLogon, &lpszFileName);
  567. if (HR_FAILED(hResult))
  568. {
  569. goto err;
  570. }
  571. /*
  572. * Replace the old one with this
  573. */
  574. lpIVTAbc->lpFreeBuff(lpIVTAbc->lpszFileName);
  575. lpIVTAbc->lpszFileName = lpszFileName;
  576. lpszFileName = NULL;
  577. }
  578. /*
  579. * File is not open so lets try to open it
  580. */
  581. lpIVTAbc->hFile = CreateFile(
  582. lpIVTAbc->lpszFileName,
  583. GENERIC_READ,
  584. FILE_SHARE_READ|FILE_SHARE_WRITE,
  585. NULL,
  586. OPEN_EXISTING,
  587. FILE_ATTRIBUTE_NORMAL,
  588. NULL);
  589. if (lpIVTAbc->hFile == INVALID_HANDLE_VALUE)
  590. {
  591. /*
  592. * The file didn't open...
  593. */
  594. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  595. SetErrorIDS(lpIVTAbc, hResult, IDS_CANT_OPEN_FAB_FILE);
  596. goto err;
  597. }
  598. }
  599. /*
  600. * Get the time and date stamp
  601. */
  602. if (!GetFileTime(lpIVTAbc->hFile, NULL, NULL, &filetime))
  603. {
  604. if (GetLastError() != NO_ERROR)
  605. {
  606. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  607. SetErrorIDS(lpIVTAbc, hResult, IDS_FAB_FILE_ATTRIB);
  608. }
  609. goto err;
  610. }
  611. /* Get the size of the file */
  612. if ((ulSize = GetFileSize(lpIVTAbc->hFile, &ulSizeHigh)) == 0xFFFFFFFF)
  613. {
  614. /*
  615. * MAYBE I have an error
  616. */
  617. if (GetLastError() != NO_ERROR)
  618. {
  619. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  620. SetErrorIDS(lpIVTAbc, hResult, IDS_FAB_FILE_ATTRIB);
  621. goto err;
  622. }
  623. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  624. SetErrorIDS(lpIVTAbc, hResult, IDS_FAB_TOO_LARGE);
  625. goto err;
  626. }
  627. /*
  628. * Actual number of valid positions
  629. */
  630. ulMaxPos = (ulSize / SIZEOF(ABCREC));
  631. /*
  632. * Check to see if it's an exact multiple of sizeof(ABCREC)
  633. */
  634. if (ulMaxPos * SIZEOF(ABCREC) != ulSize)
  635. {
  636. hResult = ResultFromScode(MAPI_E_UNABLE_TO_COMPLETE);
  637. SetErrorIDS(lpIVTAbc, hResult, IDS_FAB_CORRUPT);
  638. goto err;
  639. }
  640. /*
  641. * if the file has changed set new position, reset bookmarks etc and
  642. * notify everybody on the advise list
  643. */
  644. if (CompareFileTime(&filetime, &lpIVTAbc->filetime) || ulMaxPos != lpIVTAbc->ulMaxPos)
  645. {
  646. ULONG ulBK;
  647. ABCREC abcrec;
  648. ULONG cbT;
  649. LPMAPIADVISESINK *ppadvise;
  650. ULONG cAdvises;
  651. NOTIFICATION notif;
  652. /* save new max position and filetime */
  653. lpIVTAbc->filetime = filetime;
  654. lpIVTAbc->ulMaxPos = ulMaxPos;
  655. /* if current position is past the end of file set it to the end */
  656. if (lpIVTAbc->ulPosition > lpIVTAbc->ulMaxPos)
  657. lpIVTAbc->ulPosition = lpIVTAbc->ulMaxPos;
  658. if (ulMaxPos)
  659. {
  660. SetFilePointer(lpIVTAbc->hFile, (ulMaxPos - 1)*sizeof(ABCREC), NULL, FILE_BEGIN);
  661. /* Read in the record at that location */
  662. if ( !ReadFile(lpIVTAbc->hFile, (LPVOID) &abcrec,
  663. SIZEOF(ABCREC), &cbT, NULL)
  664. )
  665. {
  666. hResult = ResultFromScode(MAPI_E_DISK_ERROR);
  667. SetErrorIDS(lpIVTAbc, hResult, IDS_FAB_NO_READ);
  668. goto err;
  669. }
  670. /* if any of the bookmarks are past the end of file
  671. * set the file time to current file time, the position to last
  672. * record and record to last record
  673. */
  674. for (ulBK = 0; ulBK < MAX_BOOKMARKS; ulBK++)
  675. if (lpIVTAbc->rglpABCBK[ulBK] &&
  676. lpIVTAbc->rglpABCBK[ulBK]->ulPosition > lpIVTAbc->ulMaxPos)
  677. {
  678. lpIVTAbc->rglpABCBK[ulBK]->ulPosition = ulMaxPos - 1;
  679. lpIVTAbc->rglpABCBK[ulBK]->filetime = filetime;
  680. lpIVTAbc->rglpABCBK[ulBK]->abcrec = abcrec;
  681. }
  682. /*
  683. * Reallocate the checked&matched arrays
  684. */
  685. cbT = (lpIVTAbc->ulMaxPos) / 8 + 1; /* Number of bytes in both arrays */
  686. /* Reallocate ANR bitmaps */
  687. if (lpIVTAbc->rgChecked)
  688. {
  689. lpIVTAbc->rgChecked = lpIVTAbc->lpMalloc->lpVtbl->Realloc(
  690. lpIVTAbc->lpMalloc,
  691. lpIVTAbc->rgChecked,
  692. cbT);
  693. }
  694. if (lpIVTAbc->rgMatched)
  695. {
  696. lpIVTAbc->rgMatched = lpIVTAbc->lpMalloc->lpVtbl->Realloc(
  697. lpIVTAbc->lpMalloc,
  698. lpIVTAbc->rgMatched,
  699. cbT);
  700. }
  701. }
  702. else
  703. {
  704. /* if any of the bookmarks are past the end of file
  705. * set the file time to current file time, the position to the
  706. * beginning of the file.
  707. */
  708. for (ulBK = 0; ulBK < MAX_BOOKMARKS; ulBK++)
  709. if (lpIVTAbc->rglpABCBK[ulBK] &&
  710. lpIVTAbc->rglpABCBK[ulBK]->ulPosition > lpIVTAbc->ulMaxPos)
  711. {
  712. lpIVTAbc->rglpABCBK[ulBK]->ulPosition = 0;
  713. lpIVTAbc->rglpABCBK[ulBK]->filetime = filetime;
  714. }
  715. /* free the ANR bitmaps */
  716. FreeANRBitmaps(lpIVTAbc);
  717. }
  718. /* initialize the notification */
  719. RtlZeroMemory(&notif, SIZEOF(NOTIFICATION));
  720. notif.ulEventType = fnevTableModified;
  721. notif.info.tab.ulTableEvent = TABLE_CHANGED;
  722. /* notify everyone that the table has changed */
  723. for( ppadvise = lpIVTAbc->parglpAdvise, cAdvises = 0;
  724. cAdvises < lpIVTAbc->cAdvise;
  725. ++ppadvise, ++cAdvises
  726. )
  727. {
  728. Assert(*ppadvise);
  729. if (ppadvise)
  730. (void)(*ppadvise)->lpVtbl->OnNotify(*ppadvise, 1, &notif);
  731. }
  732. }
  733. out:
  734. DebugTraceResult(NewIVTAbc, hResult);
  735. return hResult;
  736. err:
  737. lpIVTAbc->lpFreeBuff(lpszFileName);
  738. goto out;
  739. }
  740. /*************************************************************************
  741. *
  742. - fIVTAbcIdleRoutine
  743. -
  744. * This function called during idle time closes the .FAB file and notifies
  745. * everyone on the advise list if the file name has changed
  746. *
  747. */
  748. BOOL STDAPICALLTYPE
  749. fIVTAbcIdleRoutine(LPVOID lpv)
  750. {
  751. LPIVTABC lpIVTAbc = (LPIVTABC) lpv;
  752. Assert(lpv);
  753. /* if file is open close it */
  754. if (lpIVTAbc->hFile != INVALID_HANDLE_VALUE)
  755. {
  756. CloseHandle(lpIVTAbc->hFile);
  757. lpIVTAbc->hFile = INVALID_HANDLE_VALUE;
  758. }
  759. /* has file name has changed? */
  760. if (!FEqualFABFiles(lpIVTAbc->lpABLogon, lpIVTAbc->lpszFileName))
  761. {
  762. /* file name has changed so call HrOpenFile to reset bookmarks etc */
  763. if (!HR_FAILED(HrOpenFile(lpIVTAbc)))
  764. {
  765. /* close the file */
  766. CloseHandle(lpIVTAbc->hFile);
  767. lpIVTAbc->hFile = INVALID_HANDLE_VALUE;
  768. }
  769. }
  770. return TRUE;
  771. }
  772. /*************************************************************************
  773. *
  774. - FreeANRBitmaps
  775. -
  776. * Frees the two ANR bitmaps associated with this table
  777. *
  778. *
  779. */
  780. void
  781. FreeANRBitmaps(LPIVTABC lpIVTAbc)
  782. {
  783. if (lpIVTAbc->rgChecked)
  784. {
  785. lpIVTAbc->lpMalloc->lpVtbl->Free(lpIVTAbc->lpMalloc, lpIVTAbc->rgChecked);
  786. lpIVTAbc->rgChecked = NULL;
  787. }
  788. if (lpIVTAbc->rgMatched)
  789. {
  790. lpIVTAbc->lpMalloc->lpVtbl->Free(lpIVTAbc->lpMalloc, lpIVTAbc->rgMatched);
  791. lpIVTAbc->rgMatched = NULL;
  792. }
  793. }
  794. /*
  795. * FCharInString
  796. *
  797. * Finds a character in a string
  798. */
  799. BOOL
  800. FCharInString(LPTSTR lpsz, TCHAR ch)
  801. {
  802. while (*lpsz && *lpsz != ch)
  803. lpsz++;
  804. return (*lpsz == ch);
  805. }