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.

5196 lines
133 KiB

  1. /*============================================================================
  2. *
  3. * ITABLE.C
  4. *
  5. * MAPI 1.0 In-memory MAPI Table DLL (MAPIU.DLL)
  6. *
  7. * Copyright (C) 1993 and 1994 Microsoft Corporation
  8. *
  9. *
  10. * Hungarian shorthand:
  11. * To avoid excessively long identifier names, the following
  12. * shorthand expressions are used:
  13. *
  14. * LPSPropTagArray lppta
  15. * LPSRestriction lpres
  16. * LPSPropValue lpprop
  17. * LPSRow lprow
  18. * LPSRowSet lprows
  19. * LPSSortOrder lpso
  20. * LPSSortOrderSet lpsos
  21. *
  22. * Known bugs:
  23. * - Restriction evaulation/copying is recursive
  24. * - Uses hinst derived from static module name (Raid 1263)
  25. */
  26. #include "_apipch.h"
  27. void FixupColsWA(LPSPropTagArray lpptaCols, BOOL bUnicodeTable);
  28. /*============================================================================
  29. * TAD (table data class)
  30. *
  31. * Implementes in-memory table data object.
  32. */
  33. TAD_Vtbl vtblTAD =
  34. {
  35. VTABLE_FILL
  36. (TAD_QueryInterface_METHOD FAR *) UNKOBJ_QueryInterface,
  37. (TAD_AddRef_METHOD FAR *) UNKOBJ_AddRef,
  38. TAD_Release,
  39. TAD_HrGetView,
  40. TAD_HrModifyRow,
  41. TAD_HrDeleteRow,
  42. TAD_HrQueryRow,
  43. TAD_HrEnumRow,
  44. TAD_HrNotify, //$PERFORMANCE
  45. TAD_HrInsertRow,
  46. TAD_HrModifyRows,
  47. TAD_HrDeleteRows
  48. };
  49. LPIID rglpiidTAD[2] =
  50. {
  51. (LPIID)&IID_IMAPITableData,
  52. (LPIID)&IID_IUnknown
  53. };
  54. /*============================================================================
  55. * VUE (table view class)
  56. *
  57. * Implementes in-memory IMAPITable class on top of TADs
  58. */
  59. VUE_Vtbl vtblVUE =
  60. {
  61. VTABLE_FILL
  62. (VUE_QueryInterface_METHOD FAR *) UNKOBJ_QueryInterface,
  63. (VUE_AddRef_METHOD FAR *) UNKOBJ_AddRef,
  64. VUE_Release,
  65. (VUE_GetLastError_METHOD FAR *) UNKOBJ_GetLastError,
  66. VUE_Advise,
  67. VUE_Unadvise,
  68. VUE_GetStatus,
  69. VUE_SetColumns,
  70. VUE_QueryColumns,
  71. VUE_GetRowCount,
  72. VUE_SeekRow,
  73. VUE_SeekRowApprox,
  74. VUE_QueryPosition,
  75. VUE_FindRow,
  76. VUE_Restrict,
  77. VUE_CreateBookmark,
  78. VUE_FreeBookmark,
  79. VUE_SortTable,
  80. VUE_QuerySortOrder,
  81. VUE_QueryRows,
  82. VUE_Abort,
  83. VUE_ExpandRow,
  84. VUE_CollapseRow,
  85. VUE_WaitForCompletion,
  86. VUE_GetCollapseState,
  87. VUE_SetCollapseState
  88. };
  89. LPIID rglpiidVUE[2] =
  90. {
  91. (LPIID)&IID_IMAPITable,
  92. (LPIID)&IID_IUnknown
  93. };
  94. /*============================================================================
  95. - CreateTable()
  96. -
  97. *
  98. * ulFlags - 0 or MAPI_UNICODE
  99. */
  100. //
  101. // BUGBUG
  102. // [PaulHi] 4/5/99 @bug
  103. // A zero is passed in to CreateTableData() ulFlags parameter. This means that the
  104. // requested table is ALWAYS ANSI and CANNOT be UNICODE, regardless of the properties
  105. // passed in through the LPSPropTagArray. The CreateTableData() function will forcibly
  106. // set the property types to PT_STRING8 or PT_UNICODE depending on the ulFlags and since
  107. // the ulFlags is hard coded to zero this means always STRING8 string properties.
  108. //
  109. STDAPI_(SCODE)
  110. CreateTable(LPCIID lpiid,
  111. ALLOCATEBUFFER FAR * lpfAllocateBuffer,
  112. ALLOCATEMORE FAR * lpfAllocateMore,
  113. FREEBUFFER FAR * lpfFreeBuffer,
  114. LPVOID lpvReserved,
  115. ULONG ulTableType,
  116. ULONG ulPropTagIndexCol,
  117. LPSPropTagArray lpptaCols,
  118. LPTABLEDATA FAR * lplptad)
  119. {
  120. return(CreateTableData(lpiid,
  121. lpfAllocateBuffer,
  122. lpfAllocateMore,
  123. lpfFreeBuffer,
  124. lpvReserved,
  125. ulTableType,
  126. ulPropTagIndexCol,
  127. lpptaCols,
  128. NULL, // lpvDataSource
  129. 0, // cbDataSource
  130. NULL,
  131. 0, // ulFlags, includes MAPI_UNICODE, which is hard coded to ANSI!!!
  132. lplptad));
  133. }
  134. /*
  135. -
  136. - CreateTableData
  137. *
  138. * ulFlags - 0 | MAPI_UNICODE | WAB_PROFILE_CONTENTS | WAB_ENABLE_PROFILES
  139. *
  140. */
  141. STDAPI_(SCODE)
  142. CreateTableData(LPCIID lpiid,
  143. ALLOCATEBUFFER FAR * lpfAllocateBuffer,
  144. ALLOCATEMORE FAR * lpfAllocateMore,
  145. FREEBUFFER FAR * lpfFreeBuffer,
  146. LPVOID lpvReserved,
  147. ULONG ulTableType,
  148. ULONG ulPropTagIndexCol,
  149. LPSPropTagArray lpptaCols,
  150. LPVOID lpvDataSource,
  151. ULONG cbDataSource,
  152. LPSBinary pbinContEID,
  153. ULONG ulFlags,
  154. LPTABLEDATA FAR * lplptad)
  155. {
  156. LPTAD lptad = NULL;
  157. SCODE sc;
  158. ULONG ulIndexType = PROP_TYPE(ulPropTagIndexCol);
  159. #if !defined(NO_VALIDATION)
  160. if ( lpiid && IsBadReadPtr(lpiid,sizeof(IID)) ||
  161. IsBadCodePtr((FARPROC)lpfAllocateBuffer) ||
  162. IsBadCodePtr((FARPROC)lpfAllocateMore) ||
  163. IsBadCodePtr((FARPROC)lpfFreeBuffer) ||
  164. (ulTableType != TBLTYPE_SNAPSHOT &&
  165. ulTableType != TBLTYPE_KEYSET &&
  166. ulTableType != TBLTYPE_DYNAMIC) ||
  167. !PROP_ID(ulPropTagIndexCol) ||
  168. (ulIndexType == PT_UNSPECIFIED) ||
  169. (ulIndexType == PT_NULL) ||
  170. (ulIndexType == PT_ERROR) ||
  171. (ulIndexType & MV_FLAG) ||
  172. FBadColumnSet(lpptaCols) ||
  173. IsBadWritePtr(lplptad,sizeof(LPTABLEDATA)) )
  174. {
  175. DebugTrace(TEXT("CreateTable() - Bad parameter(s) passed\n") );
  176. return MAPI_E_INVALID_PARAMETER;
  177. }
  178. #endif
  179. // Verify caller wants an IMAPITableData interface
  180. if ( lpiid && memcmp(lpiid, &IID_IMAPITableData, sizeof(IID)) )
  181. {
  182. DebugTrace(TEXT("CreateTable() - Unknown interface ID passed\n") );
  183. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  184. }
  185. // Instantiate a new table data object
  186. if ( FAILED(sc = lpfAllocateBuffer(sizeof(TAD), (LPVOID FAR *) &lptad)) )
  187. {
  188. DebugTrace(TEXT("CreateTable() - Error instantiating new TAD (SCODE = 0x%08lX)\n"), sc );
  189. goto err;
  190. }
  191. MAPISetBufferName(lptad, TEXT("ITable TAD object"));
  192. ZeroMemory(lptad, sizeof(TAD));
  193. if (lpvDataSource) {
  194. if (cbDataSource) {
  195. LPTSTR lpNew;
  196. if (! (lpNew = LocalAlloc(LPTR, cbDataSource))) {
  197. DebugTrace(TEXT("CreateTable:LocalAlloc(%u) -> %u\n"), cbDataSource, GetLastError());
  198. sc = MAPI_E_NOT_ENOUGH_MEMORY;
  199. goto err;
  200. }
  201. CopyMemory(lpNew, lpvDataSource, cbDataSource);
  202. lptad->lpvDataSource = lpNew;
  203. } else {
  204. lptad->lpvDataSource = lpvDataSource; // no size, just a pointer. DON'T Free!
  205. }
  206. lptad->cbDataSource = cbDataSource;
  207. } else {
  208. lptad->cbDataSource = 0;
  209. lptad->lpvDataSource = NULL;
  210. }
  211. lptad->pbinContEID = pbinContEID;
  212. //if(!pbinContEID || (!pbinContEID->cb && !pbinContEID->lpb)) // This is the PAB container
  213. // The caller will send in container EIDs which will be:
  214. // if PAB = User Folder, cont EID won't be NULL - return folder contents only
  215. // if PAB = Virtual Folder, cont EID will have 0 and NULL in it - return all WAB contents
  216. // if WAB_PROFILE_CONTENTS is specified, just return all the contents of all the folders in the profile
  217. if(ulFlags & WAB_PROFILE_CONTENTS)
  218. lptad->bAllProfileContents = TRUE; // this forces folder contents only
  219. if(ulFlags & MAPI_UNICODE)
  220. lptad->bMAPIUnicodeTable = TRUE;
  221. if(pbinContEID && pbinContEID->cb && pbinContEID->lpb) // This is not the Virtual PAB container
  222. lptad->bContainerContentsOnly = (ulFlags & WAB_ENABLE_PROFILES);
  223. else
  224. lptad->bContainerContentsOnly = FALSE;
  225. lptad->inst.lpfAllocateBuffer = lpfAllocateBuffer;
  226. lptad->inst.lpfAllocateMore = lpfAllocateMore;
  227. lptad->inst.lpfFreeBuffer = lpfFreeBuffer;
  228. #ifdef MAC
  229. lptad->inst.hinst = hinstMapiX;//GetCurrentProcess();
  230. #else
  231. lptad->inst.hinst = hinstMapiX;//HinstMapi();
  232. #ifdef DEBUG
  233. if (lptad->inst.hinst == NULL)
  234. TraceSz1( TEXT("ITABLE: GetModuleHandle failed with error %08lX"),
  235. GetLastError());
  236. #endif /* DEBUG */
  237. #endif /* MAC */
  238. if (FAILED(sc = UNKOBJ_Init( (LPUNKOBJ) lptad
  239. , (UNKOBJ_Vtbl FAR *) &vtblTAD
  240. , sizeof(vtblTAD)
  241. , rglpiidTAD
  242. , sizeof(rglpiidTAD)/sizeof(REFIID)
  243. , &lptad->inst)))
  244. {
  245. DebugTrace(TEXT("CreateTable() - Error initializing object (SCODE = 0x%08lX)\n"), sc );
  246. goto err;
  247. }
  248. lptad->ulTableType = ulTableType;
  249. lptad->ulPropTagIndexCol = ulPropTagIndexCol;
  250. if ( FAILED(sc = ScCOAllocate(lptad,
  251. CbNewSPropTagArray(lpptaCols->cValues),
  252. &lptad->lpptaCols)) )
  253. {
  254. DebugTrace(TEXT("CreateTable() - Error duping initial column set (SCODE = 0x%08lX)\n"), sc );
  255. goto err;
  256. }
  257. lptad->ulcColsMac = lpptaCols->cValues;
  258. CopyMemory(lptad->lpptaCols,
  259. lpptaCols,
  260. (size_t) (CbNewSPropTagArray(lpptaCols->cValues)));
  261. // [PaulHi] 4/5/99 @comment Shouldn't
  262. // clients of the WAB request ANSI/UNICODE based on property tags in the
  263. // column array, rather than doing mass conversions?
  264. // Seems like the fix is to create two versions of column property arrays,
  265. // an ANSI and Unicode version.
  266. FixupColsWA(lptad->lpptaCols, (ulFlags & MAPI_UNICODE));
  267. // And return it
  268. *lplptad = (LPTABLEDATA) lptad;
  269. ret:
  270. DebugTraceSc(CreateTable, sc);
  271. return sc;
  272. err:
  273. UlRelease(lptad);
  274. goto ret;
  275. }
  276. /*============================================================================
  277. - TAD::Release()
  278. -
  279. */
  280. STDMETHODIMP_(ULONG)
  281. TAD_Release( LPTAD lptad )
  282. {
  283. ULONG ulcRef;
  284. LPSRow * plprow;
  285. #if !defined(NO_VALIDATION)
  286. if ( BAD_STANDARD_OBJ(lptad,TAD_,Release,lpVtbl) )
  287. {
  288. TraceSz( TEXT("TAD::Release() - Invalid parameter passed as TAD object"));
  289. return !0;
  290. }
  291. #endif
  292. LockObj(lptad);
  293. if (ulcRef = lptad->ulcRef)
  294. {
  295. ulcRef = --lptad->ulcRef;
  296. }
  297. if ( ulcRef == 0 && !lptad->lpvueList )
  298. {
  299. UnlockObj(lptad); //$ Do we need this?
  300. COFree(lptad, lptad->lpptaCols);
  301. if (lptad->cbDataSource && lptad->lpvDataSource) {
  302. LocalFreeAndNull(&lptad->lpvDataSource);
  303. }
  304. plprow = lptad->parglprowAdd + lptad->ulcRowsAdd;
  305. while ( plprow-- > lptad->parglprowAdd )
  306. ScFreeBuffer(lptad, *plprow);
  307. COFree(lptad, lptad->parglprowAdd);
  308. COFree(lptad, lptad->parglprowIndex);
  309. UNKOBJ_Deinit((LPUNKOBJ) lptad);
  310. ScFreeBuffer(lptad, lptad);
  311. }
  312. else
  313. {
  314. #if DEBUG
  315. if ( ulcRef == 0 && lptad->lpvueList )
  316. {
  317. TraceSz( TEXT("TAD::Release() - TAD object still has open views"));
  318. }
  319. #endif // DEBUG
  320. UnlockObj(lptad);
  321. }
  322. return ulcRef;
  323. }
  324. /*============================================================================
  325. - TAD::HrGetView()
  326. -
  327. * A NULL lpsos means that rows will be in the order that they were added
  328. * to the TAD.
  329. */
  330. STDMETHODIMP
  331. TAD_HrGetView(
  332. LPTAD lptad,
  333. LPSSortOrderSet lpsos,
  334. CALLERRELEASE FAR * lpfReleaseCallback,
  335. ULONG ulReleaseData,
  336. LPMAPITABLE FAR * lplpmt )
  337. {
  338. SCODE sc;
  339. LPVUE lpvue = NULL;
  340. #if !defined(NO_VALIDATION)
  341. VALIDATE_OBJ(lptad,TAD_,HrGetView,lpVtbl);
  342. if ( (lpsos && FBadSortOrderSet(lpsos)) ||
  343. (lpfReleaseCallback && IsBadCodePtr((FARPROC) lpfReleaseCallback)) ||
  344. IsBadWritePtr(lplpmt, sizeof(LPMAPITABLE)) )
  345. {
  346. DebugTrace(TEXT("TAD::HrGetView() - Invalid parameter(s) passed\n") );
  347. return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  348. }
  349. #endif
  350. // Can't support categories
  351. if (lpsos && lpsos->cCategories)
  352. {
  353. DebugTrace(TEXT("TAD::GetView() - No support for categories\n") );
  354. return ResultFromScode(MAPI_E_TOO_COMPLEX);
  355. }
  356. LockObj(lptad);
  357. // Instantiate a new table view
  358. if ( FAILED(sc = lptad->inst.lpfAllocateBuffer(sizeof(VUE),
  359. (LPVOID FAR *) &lpvue)) )
  360. {
  361. DebugTrace(TEXT("ScCreateView() - Error instantiating VUE on TAD (SCODE = 0x%08lX)\n"), sc );
  362. goto ret;
  363. }
  364. MAPISetBufferName(lpvue, TEXT("ITable VUE object"));
  365. ZeroMemory(lpvue, sizeof(VUE));
  366. if (FAILED(sc = UNKOBJ_Init( (LPUNKOBJ) lpvue
  367. , (UNKOBJ_Vtbl FAR *) &vtblVUE
  368. , sizeof(vtblVUE)
  369. , rglpiidVUE
  370. , sizeof(rglpiidVUE)/sizeof(REFIID)
  371. , lptad->pinst)))
  372. {
  373. DebugTrace(TEXT("ScCreateView() - Error initializing VUE object (SCODE = 0x%08lX)\n"), sc );
  374. // don't try to release the vue since it wasn't initialized yet
  375. lptad->inst.lpfFreeBuffer(lpvue);
  376. goto ret;
  377. }
  378. // Link the view to the TAD and AddRef the TAD.
  379. lpvue->lpvueNext = lptad->lpvueList;
  380. lptad->lpvueList = lpvue;
  381. lpvue->lptadParent = lptad;
  382. UlAddRef(lptad);
  383. // Identifier for this table
  384. lpvue->cbDataSource = lptad->cbDataSource;
  385. lpvue->lpvDataSource = lptad->lpvDataSource;
  386. // Initialize the predefined bookmarks
  387. lpvue->bkBeginning.dwfBKS = dwfBKSValid;
  388. lpvue->bkCurrent.dwfBKS = dwfBKSValid;
  389. lpvue->bkEnd.dwfBKS = dwfBKSValid;
  390. #ifdef NOTIFICATIONS
  391. // Burn up a MUID for the notification key for this view
  392. if ( FAILED(sc = ScGenerateMuid(&lpvue->mapiuidNotif)) )
  393. {
  394. DebugTrace(TEXT("TAD::HrGetView() - Error generating MUID for notification key (SCODE = 0x%08lX)\n"), sc );
  395. goto err;
  396. }
  397. #endif
  398. // Make a copy of the initial sort order for the VUE
  399. if ( lpsos
  400. && FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lptad
  401. , CbSSortOrderSet(lpsos)
  402. , (LPBYTE) lpsos
  403. , 0
  404. , (LPBYTE FAR *) &(lpvue->lpsos))) )
  405. {
  406. DebugTrace(TEXT("TAD::GetView() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc );
  407. goto err;
  408. }
  409. MAPISetBufferName(lpvue->lpsos, TEXT("ITable: dup sort order set"));
  410. // Load the view's initial row set in sorted order
  411. if ( FAILED(sc = ScLoadRows(lpvue->lptadParent->ulcRowsAdd,
  412. lpvue->lptadParent->parglprowAdd,
  413. lpvue,
  414. NULL,
  415. lpvue->lpsos)) )
  416. {
  417. DebugTrace(TEXT("TAD::HrGetView() - Error loading view's initial row set (SCODE = 0x%08lX)\n"), sc );
  418. goto err;
  419. }
  420. lpvue->bMAPIUnicodeTable = lptad->bMAPIUnicodeTable;
  421. // Set the view's initial column set
  422. if ( FAILED(sc = GetScode(VUE_SetColumns(lpvue, lptad->lpptaCols, 0))) )
  423. {
  424. DebugTrace(TEXT("TAD::HrGetView() - Error setting view's initial column set (SCODE = 0x%08lX)\n"), sc );
  425. goto err;
  426. }
  427. lpvue->lpfReleaseCallback = lpfReleaseCallback;
  428. lpvue->ulReleaseData = ulReleaseData;
  429. *lplpmt = (LPMAPITABLE) lpvue;
  430. ret:
  431. UnlockObj(lptad);
  432. return ResultFromScode(sc);
  433. err:
  434. // This will unlink and release the parent TAD.
  435. UlRelease(lpvue);
  436. goto ret;
  437. }
  438. /*============================================================================
  439. - TAD::HrModifyRow()
  440. -
  441. */
  442. STDMETHODIMP
  443. TAD_HrModifyRow(
  444. LPTAD lptad,
  445. LPSRow lprow )
  446. {
  447. SizedSRowSet( 1, rowsetIn);
  448. rowsetIn.cRows = 1;
  449. rowsetIn.aRow[0] = *lprow;
  450. return TAD_HrModifyRows(lptad, 0, (LPSRowSet) &rowsetIn);
  451. }
  452. /*============================================================================
  453. - TAD::HrModifyRows()
  454. -
  455. */
  456. STDMETHODIMP
  457. TAD_HrModifyRows(
  458. LPTAD lptad,
  459. ULONG ulFlags,
  460. LPSRowSet lprowsetIn )
  461. {
  462. ULONG cRowsCopy = 0;
  463. LPSRow * parglprowSortedCopy = NULL;
  464. LPSRow * parglprowUnsortedCopy = NULL;
  465. ULONG cRowsOld = 0;
  466. LPSRow * parglprowOld = NULL;
  467. ULONG cNewTags = 0;
  468. SCODE sc;
  469. #if !defined(NO_VALIDATION)
  470. VALIDATE_OBJ(lptad,TAD_,HrModifyRows,lpVtbl);
  471. if ( IsBadReadPtr( lprowsetIn, CbNewSRowSet(0))
  472. || IsBadWritePtr( lprowsetIn, CbSRowSet(lprowsetIn)))
  473. {
  474. DebugTrace(TEXT("TAD::HrModifyRows() - Invalid parameter(s) passed\n") );
  475. return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  476. }
  477. // Validation of the actual rows input will be done at the same time that
  478. // we make our internal copy.
  479. #endif
  480. if (ulFlags)
  481. {
  482. DebugTrace(TEXT("TAD::HrModifyRows() - Unknown flags passed\n") );
  483. // return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
  484. }
  485. LockObj(lptad);
  486. // Make a copy of the rows for our own use.
  487. // Add new columns to the TAD's column set.
  488. //
  489. // Move the index column to the front.
  490. // Filter out PT_ERROR and PT_NULL columns.
  491. // Validate the input rows.
  492. // Note - two rows with the same index property is invalid.
  493. if ( FAILED(sc = ScCopyTadRowSet( lptad
  494. , lprowsetIn
  495. , &cNewTags
  496. , &cRowsCopy
  497. , &parglprowUnsortedCopy
  498. , &parglprowSortedCopy)) )
  499. {
  500. DebugTrace(TEXT("TAD::HrModifyRows() - Error duping row set to modify\n") );
  501. goto ret;
  502. }
  503. // Replace/add the copied row to the table data. We pass in the unsorted
  504. // Set in order to maintain the FIFO behaviour on unsorted views
  505. // Note! This call MUST replace all (SUCCESS) or none (FAILURE)!
  506. if ( FAILED(sc = ScReplaceRows( lptad
  507. , cRowsCopy
  508. , parglprowUnsortedCopy
  509. , &cRowsOld
  510. , &parglprowOld)) )
  511. {
  512. DebugTrace(TEXT("TAD::HrModifyRows() - Error adding rows (SCODE = 0x%08lX)\n"), sc );
  513. goto err;
  514. }
  515. // Update the views with the modified rows.
  516. // NOTE! Failure to update a view CANNOT leave a view pointing to an
  517. // old row!
  518. UpdateViews( lptad
  519. , cRowsOld
  520. , parglprowOld
  521. , cRowsCopy
  522. , parglprowUnsortedCopy
  523. , parglprowSortedCopy);
  524. // Free the old rows.
  525. if (parglprowOld)
  526. {
  527. LPSRow * plprowTmp = parglprowOld;
  528. while (cRowsOld)
  529. {
  530. ScFreeBuffer( lptad, *(plprowTmp++));
  531. cRowsOld--;
  532. }
  533. }
  534. ret:
  535. // Free the tables of row pointers
  536. ScFreeBuffer( lptad, parglprowSortedCopy);
  537. ScFreeBuffer( lptad, parglprowUnsortedCopy);
  538. ScFreeBuffer(lptad, parglprowOld);
  539. UnlockObj(lptad);
  540. return ResultFromScode(sc);
  541. err:
  542. // Reset the TAD columns
  543. lptad->lpptaCols->cValues -= cNewTags;
  544. // On error the all copied rows are freed...
  545. if (parglprowSortedCopy)
  546. {
  547. LPSRow * plprowTmp = parglprowSortedCopy;
  548. while (cRowsCopy)
  549. {
  550. ScFreeBuffer( lptad, *(plprowTmp++));
  551. cRowsCopy--;
  552. }
  553. }
  554. goto ret;
  555. }
  556. /*============================================================================
  557. - TAD::HrDeleteRows()
  558. -
  559. */
  560. STDMETHODIMP
  561. TAD_HrDeleteRows(
  562. LPTAD lptad,
  563. ULONG ulFlags,
  564. LPSRowSet lprowsetToDelete,
  565. ULONG FAR * lpcRowsDeleted )
  566. {
  567. SCODE sc = S_OK;
  568. LPSRow lprowDelete;
  569. LPSRow * plprowIn;
  570. LPSRow * plprowOut;
  571. ULONG cRowsDeleted = 0;
  572. LPSRow * parglprowOld = NULL;
  573. LPSRow * * pargplprowOld;
  574. #if !defined(NO_VALIDATION)
  575. VALIDATE_OBJ(lptad,TAD_,HrDeleteRow,lpVtbl);
  576. if ( ((ulFlags & TAD_ALL_ROWS) && lprowsetToDelete)
  577. || ( !(ulFlags & TAD_ALL_ROWS)
  578. && ( IsBadReadPtr( lprowsetToDelete, CbNewSRowSet(0))
  579. || IsBadWritePtr( lprowsetToDelete
  580. , CbSRowSet(lprowsetToDelete))))
  581. || (lpcRowsDeleted && IsBadWritePtr( lpcRowsDeleted, sizeof(ULONG))))
  582. {
  583. DebugTrace(TEXT("TAD::HrDeleteRows() - Invalid parameter(s) passed\n") );
  584. return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  585. }
  586. #endif
  587. if (ulFlags & ~TAD_ALL_ROWS)
  588. {
  589. DebugTrace(TEXT("TAD::HrModifyRows() - Unknown flags passed\n") );
  590. // return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
  591. }
  592. LockObj(lptad);
  593. if (ulFlags & TAD_ALL_ROWS)
  594. {
  595. cRowsDeleted = lptad->ulcRowsAdd;
  596. //
  597. // If there are any rows to delete
  598. //
  599. if (cRowsDeleted)
  600. {
  601. //
  602. // And they delete cleanly
  603. //
  604. if (FAILED(sc = ScDeleteAllRows( lptad)))
  605. {
  606. DebugTrace(TEXT("TAD::HrDeleteRows() - ScDeleteAllRows returned error (SCODE = 0x%08lX)\n"), sc );
  607. goto ret;
  608. }
  609. }
  610. if (lpcRowsDeleted)
  611. {
  612. *lpcRowsDeleted = cRowsDeleted;
  613. }
  614. goto ret;
  615. }
  616. if (!lprowsetToDelete->cRows)
  617. {
  618. goto ret;
  619. }
  620. // Not allowed to delete rows from non-dynamic tables with open views
  621. if ( lptad->ulTableType != TBLTYPE_DYNAMIC && lptad->lpvueList )
  622. {
  623. DebugTrace(TEXT("TAD::HrDeleteRows() - Operation not supported on non-dynamic TAD with open views\n") );
  624. sc = MAPI_E_CALL_FAILED;
  625. goto ret;
  626. }
  627. // Allocate the list of old rows now so we won't fail after we start
  628. // adding rows.
  629. if (FAILED(sc = ScAllocateBuffer( lptad
  630. , lprowsetToDelete->cRows * sizeof(LPSRow)
  631. , &parglprowOld)))
  632. {
  633. DebugTrace(TEXT("ScAddRows() - Error creating old row list (SCODE = 0x%08lX)\n"), sc );
  634. goto ret;
  635. }
  636. MAPISetBufferName(parglprowOld, TEXT("ITable old row list"));
  637. // First we will try to find each row in the index sorted row list.
  638. // We won't delete them on the first pass since we must verify that
  639. // there aren't dups in the row set and that each row has an Index
  640. // property before we actually delete any rows.
  641. // Keep a list of pointers to the index sorted slot (argplprow) so that
  642. // we don't have to search again when we finally delete the rows.
  643. pargplprowOld = (LPSRow * *) parglprowOld;
  644. for ( lprowDelete = lprowsetToDelete->aRow + lprowsetToDelete->cRows
  645. ; lprowDelete-- > lprowsetToDelete->aRow
  646. ; )
  647. {
  648. LPSRow * plprow = NULL;
  649. LPSPropValue lppropIndex;
  650. if (FBadRow(lprowDelete))
  651. {
  652. DebugTrace(TEXT("TAD::HrDeleteRows() - Invalid row(s) passed\n") );
  653. sc = MAPI_E_INVALID_PARAMETER;
  654. goto ret;
  655. }
  656. if (!(lppropIndex = PpropFindProp( lprowDelete->lpProps
  657. , lprowDelete->cValues
  658. , lptad->ulPropTagIndexCol)))
  659. {
  660. sc = MAPI_E_INVALID_PARAMETER;
  661. DebugTrace(TEXT("TAD::HrDeleteRows() - Row has no Index property.\n") );
  662. goto ret;
  663. }
  664. sc = ScFindRow(lptad, lppropIndex, &plprow);
  665. if (sc == MAPI_E_NOT_FOUND)
  666. {
  667. // Don't try to delete rows that aren't in the table
  668. continue;
  669. }
  670. else if (FAILED(sc))
  671. {
  672. // Something bad happened in ScFindRow so fail the call
  673. DebugTrace(TEXT("TAD::HrDeleteRows() - Error from ScFindRow.\n") );
  674. goto ret;
  675. }
  676. // The row is valid and in the TAD so put its plprow in the table
  677. // to delete
  678. *(pargplprowOld++) = plprow;
  679. }
  680. sc = S_OK;
  681. // Now that we have completely validated the input row set and have made
  682. // a list of plprow's to delete from the Index sorted set, we can
  683. // actually delete them from the unsorted set and the index sorted set.
  684. // turn the list of plprows into a list of lprows for UpdateViews
  685. // The call is not allowed to fail after this point!
  686. cRowsDeleted = (ULONG) (((LPSRow *) pargplprowOld) - parglprowOld);
  687. while (((LPSRow *) pargplprowOld--) > parglprowOld)
  688. {
  689. LPSRow * plprow;
  690. LPSRow lprow = **pargplprowOld;
  691. // Remove the row from the unsorted row set
  692. if (plprow = PlprowByLprow( lptad->ulcRowsAdd
  693. , lptad->parglprowAdd
  694. , lprow))
  695. {
  696. --lptad->ulcRowsAdd;
  697. MoveMemory( plprow
  698. , plprow + 1
  699. , (size_t)
  700. ( (BYTE *)(lptad->parglprowAdd + lptad->ulcRowsAdd)
  701. - (BYTE *)(plprow)));
  702. }
  703. // The row should be in the unsorted set.
  704. Assert(plprow);
  705. // Remove the row from the Index sorted row set by
  706. // setting it to NULL. We'll squish the NULLs out later since we
  707. // don't know now what order they are in.
  708. **pargplprowOld = NULL;
  709. // Turn the plprow into an lprow to use in UpdateViews
  710. (LPSRow) (*pargplprowOld) = lprow;
  711. }
  712. // Remove the NULL pointers that were left in the Index sorted set
  713. for ( plprowOut = plprowIn = lptad->parglprowIndex
  714. ; (plprowIn < lptad->parglprowIndex + lptad->ulcRowsIndex)
  715. ; plprowIn++)
  716. {
  717. if (*plprowIn)
  718. {
  719. *plprowOut = *plprowIn;
  720. plprowOut++;
  721. }
  722. }
  723. lptad->ulcRowsIndex = (ULONG) (plprowOut - lptad->parglprowIndex);
  724. // Update and notify any affected views
  725. // using the converted argplprowOld (arglprowOld)
  726. UpdateViews(lptad, cRowsDeleted, parglprowOld, 0, NULL, NULL);
  727. if (lpcRowsDeleted)
  728. {
  729. *lpcRowsDeleted = cRowsDeleted;
  730. }
  731. ret:
  732. // Free the old rows.
  733. if (parglprowOld)
  734. {
  735. LPSRow * plprowOld;
  736. for ( plprowOld = parglprowOld + cRowsDeleted
  737. ; plprowOld-- > parglprowOld
  738. ; )
  739. {
  740. ScFreeBuffer( lptad, *plprowOld);
  741. }
  742. ScFreeBuffer(lptad, parglprowOld);
  743. }
  744. UnlockObj(lptad);
  745. return ResultFromScode(sc);
  746. }
  747. /*============================================================================
  748. - TAD::HrDeleteRow()
  749. -
  750. */
  751. STDMETHODIMP
  752. TAD_HrDeleteRow (
  753. LPTAD lptad,
  754. LPSPropValue lpprop )
  755. {
  756. HRESULT hResult;
  757. SizedSRowSet(1, rowsetToDelete);
  758. ULONG cRowsDeleted;
  759. #if !defined(NO_VALIDATION)
  760. VALIDATE_OBJ(lptad,TAD_,HrDeleteRow,lpVtbl);
  761. // Validation of lpprop done by TAD_HrDeleteRows
  762. #endif
  763. rowsetToDelete.cRows = 1;
  764. rowsetToDelete.aRow[0].cValues = 1;
  765. rowsetToDelete.aRow[0].lpProps = lpprop;
  766. if (HR_FAILED(hResult = TAD_HrDeleteRows( lptad
  767. , 0
  768. , (LPSRowSet) &rowsetToDelete
  769. , &cRowsDeleted)))
  770. {
  771. DebugTrace(TEXT("TAD::HrDeleteRow() - Failed to delete rows.\n") );
  772. goto ret;
  773. }
  774. Assert((cRowsDeleted == 1) || !cRowsDeleted);
  775. if (!cRowsDeleted)
  776. {
  777. DebugTrace(TEXT("TAD::HrDeleteRow() - Couldn't find row to delete.\n") );
  778. hResult = ResultFromScode(MAPI_E_NOT_FOUND);
  779. }
  780. ret:
  781. return hResult;
  782. }
  783. /*============================================================================
  784. - TAD::HrQueryRow()
  785. -
  786. */
  787. STDMETHODIMP
  788. TAD_HrQueryRow(
  789. LPTAD lptad,
  790. LPSPropValue lpprop,
  791. LPSRow FAR * lplprow,
  792. ULONG * puliRow)
  793. {
  794. LPSRow * plprow = NULL;
  795. SCODE sc;
  796. #if !defined(NO_VALIDATION)
  797. VALIDATE_OBJ(lptad,TAD_,HrQueryRow,lpVtbl);
  798. if ( FBadProp(lpprop) ||
  799. IsBadWritePtr(lplprow, sizeof(LPSRow)) ||
  800. (puliRow && IsBadWritePtr(puliRow, sizeof(*puliRow))) )
  801. {
  802. DebugTrace(TEXT("TAD::HrQueryRow() - Invalid parameter(s) passed\n") );
  803. return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  804. }
  805. #endif
  806. LockObj(lptad);
  807. // Find the row
  808. if (FAILED(sc = ScFindRow(lptad, lpprop, &plprow)))
  809. {
  810. goto ret;
  811. }
  812. Assert(plprow);
  813. // Copy the row to return. Don't try to add new tags to the column set.
  814. if ( FAILED(sc = ScCopyTadRow( lptad, *plprow, NULL, lplprow )) )
  815. {
  816. DebugTrace(TEXT("TAD::HrQueryRow() - Error making copy of row (SCODE = 0x%08lX)\n"), sc );
  817. goto ret;
  818. }
  819. if (puliRow)
  820. {
  821. // Find the row from the unsorted row set
  822. plprow = PlprowByLprow( lptad->ulcRowsAdd,
  823. lptad->parglprowAdd,
  824. *plprow);
  825. // If the row was in the Index sorted set then it should be in
  826. // the unsorted set.
  827. Assert(plprow);
  828. *puliRow = (ULONG) (plprow - lptad->parglprowAdd);
  829. }
  830. ret:
  831. UnlockObj(lptad);
  832. return ResultFromScode(sc);
  833. }
  834. /*============================================================================
  835. - TAD::HrEnumRow()
  836. -
  837. */
  838. STDMETHODIMP
  839. TAD_HrEnumRow(
  840. LPTAD lptad,
  841. ULONG uliRow,
  842. LPSRow FAR * lplprow )
  843. {
  844. SCODE sc;
  845. #if !defined(NO_VALIDATION)
  846. VALIDATE_OBJ(lptad,TAD_,HrEnumRow,lpVtbl);
  847. if ( IsBadWritePtr(lplprow, sizeof(LPSRow)) )
  848. {
  849. DebugTrace(TEXT("TAD::HrEnumRow() - Invalid parameter(s) passed\n") );
  850. return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  851. }
  852. #endif
  853. LockObj(lptad);
  854. if ( uliRow < lptad->ulcRowsAdd )
  855. {
  856. // Copy the row to return.
  857. if ( FAILED(sc = ScCopyTadRow( lptad
  858. , lptad->parglprowAdd[uliRow]
  859. , NULL // Don't try to add new columns.
  860. , lplprow )) )
  861. {
  862. DebugTrace(TEXT("TAD::HrEnumRow() - Error making copy of row (SCODE = 0x%08lX)\n"), sc );
  863. goto ret;
  864. }
  865. }
  866. else
  867. {
  868. // Return NULL if row index is out of range
  869. *lplprow = NULL;
  870. sc = S_OK;
  871. }
  872. ret:
  873. UnlockObj(lptad);
  874. return ResultFromScode(sc);
  875. }
  876. /*============================================================================
  877. - TAD_HrNotify
  878. -
  879. * Parameters:
  880. * lptad in the table object
  881. * ulFlags in flags (unused)
  882. * cValues in number of property values
  883. * lpsv in property value array to be compared
  884. */
  885. STDMETHODIMP
  886. TAD_HrNotify(
  887. LPTAD lptad,
  888. ULONG ulFlags,
  889. ULONG cValues,
  890. LPSPropValue lpspv)
  891. {
  892. #ifdef NOTIFICATION
  893. NOTIFICATION notif;
  894. VUENOTIFKEY vuenotifkey;
  895. ULONG uliRow;
  896. ULONG ulNotifFlags;
  897. ULONG uliProp;
  898. LPSRow lprow;
  899. SCODE sc;
  900. LPVUE lpvue;
  901. #endif // NOTIFICATIONS
  902. #if !defined(NO_VALIDATION)
  903. if ( BAD_STANDARD_OBJ(lptad,TAD_,HrNotify,lpVtbl) )
  904. {
  905. DebugTrace(TEXT("TAD::HrNotify() - Invalid parameter passed as TAD object\n") );
  906. return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  907. }
  908. if (cValues > UINT_MAX/sizeof(SPropValue) || IsBadReadPtr(lpspv,((UINT)cValues*sizeof(SPropValue))))
  909. {
  910. DebugTrace(TEXT("TAD::HrNotify() - Invalid parameter passed as prop value array\n") );
  911. return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  912. }
  913. #endif
  914. if (cValues==0)
  915. {
  916. DebugTrace(TEXT("TAD::HrNotify() - zero props in prop value array?? \n") );
  917. return hrSuccess;
  918. }
  919. #ifdef NOTIFICATIONS
  920. ZeroMemory(&notif, sizeof(NOTIFICATION));
  921. notif.ulEventType = fnevTableModified;
  922. notif.info.tab.ulTableEvent = TABLE_ROW_MODIFIED;
  923. LockObj(lptad);
  924. for ( lpvue = (LPVUE) lptad->lpvueList;
  925. lpvue != NULL;
  926. lpvue = (LPVUE) lpvue->lpvueNext )
  927. {
  928. AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE)) &&
  929. lpvue->lpVtbl == &vtblVUE,
  930. TEXT("Bad lpvue in TAD vue List.") );
  931. for (uliRow=0; uliRow < lpvue->bkEnd.uliRow; uliRow++)
  932. {
  933. lprow = lpvue->parglprows[uliRow];
  934. // does the row contain matching properties?
  935. if (!FRowContainsProp(lprow,cValues,lpspv))
  936. continue; // it doesn't so go on to next row
  937. // copy the row for the client
  938. sc=ScCopyVueRow(lpvue,lpvue->lpptaCols,lprow,&notif.info.tab.row);
  939. if (FAILED(sc))
  940. {
  941. DebugTrace(TEXT("TAD_HrNotify() - VUE_ScCopyRow return %s\n"), SzDecodeScode(sc));
  942. continue;
  943. }
  944. notif.info.tab.propIndex=*lpspv;
  945. // Fill in index property of row previous to row
  946. // modified. If row modified was first
  947. // row, fill in 0 for proptag of index property of
  948. // previous row.
  949. if (uliRow == 0)
  950. {
  951. ZeroMemory(&notif.info.tab.propPrior, sizeof(SPropValue));
  952. notif.info.tab.propPrior.ulPropTag = PR_NULL;
  953. }
  954. else
  955. {
  956. // point to previous row
  957. lprow = lpvue->parglprows[uliRow-1];
  958. for (uliProp=0; uliProp < lprow->cValues; uliProp++)
  959. {
  960. if (lprow->lpProps[uliProp].ulPropTag==lpspv->ulPropTag)
  961. break;
  962. }
  963. // should have found the index property
  964. Assert(uliProp < lprow->cValues);
  965. notif.info.tab.propPrior = lprow->lpProps[uliProp];
  966. }
  967. // Kick off notifications to all the notifications open on the view
  968. vuenotifkey.ulcb = sizeof(MAPIUID);
  969. vuenotifkey.mapiuid = lpvue->mapiuidNotif;
  970. ulNotifFlags = 0;
  971. (void) HrNotify((LPNOTIFKEY) &vuenotifkey,
  972. 1,
  973. &notif,
  974. &ulNotifFlags);
  975. // Free the notification's copy of the modified row
  976. ScFreeBuffer(lpvue, notif.info.tab.row.lpProps);
  977. }
  978. }
  979. UnlockObj(lptad);
  980. return hrSuccess;
  981. #endif // NOTIFICATIONS
  982. return(ResultFromScode(MAPI_E_NO_SUPPORT));
  983. }
  984. /*============================================================================
  985. - TAD::HrInsertRow()
  986. -
  987. */
  988. STDMETHODIMP
  989. TAD_HrInsertRow(
  990. LPTAD lptad,
  991. ULONG uliRow,
  992. LPSRow lprow )
  993. {
  994. LPSRow lprowCopy = NULL;
  995. SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 };
  996. ULONG cTagsAdded = 0;
  997. LPSRow * plprow;
  998. SCODE sc;
  999. #if !defined(NO_VALIDATION)
  1000. VALIDATE_OBJ(lptad,TAD_,HrInsertRow,lpVtbl);
  1001. // lprow is validated by ScCopyTadRow()
  1002. if (uliRow > lptad->ulcRowsAdd)
  1003. {
  1004. DebugTrace(TEXT("TAD::HrInsertRow() - Invalid parameter(s) passed\n") );
  1005. DebugTrace(TEXT("TAD::HrInsertRow() - uliRow is zero or greater thna row count\n") );
  1006. return ResultFromScode(MAPI_E_INVALID_PARAMETER);
  1007. }
  1008. #endif
  1009. LockObj(lptad);
  1010. // Make a copy of the row for our own use filtering
  1011. // out PT_ERROR and PT_NULL columns and moving
  1012. // the index column to the front
  1013. if ( FAILED(sc = ScCopyTadRow(lptad, lprow, &cTagsAdded, &lprowCopy)) )
  1014. {
  1015. DebugTrace(TEXT("TAD::HrInsertRow() - Error duping row to modify\n") );
  1016. goto ret;
  1017. }
  1018. sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol;
  1019. sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND;
  1020. // Find out where the row would collate on the Index sorted set
  1021. // NOTE! We collate before the first occurance so that we will end
  1022. // up pointing at the row with this index if it exists.
  1023. plprow = PlprowCollateRow(lptad->ulcRowsIndex,
  1024. lptad->parglprowIndex,
  1025. (LPSSortOrderSet) &sosIndex,
  1026. FALSE,
  1027. lprowCopy);
  1028. // If a row with the same Index value exists then we can't insert!
  1029. if ( lptad->ulcRowsIndex
  1030. && (plprow < (lptad->parglprowIndex + lptad->ulcRowsIndex))
  1031. && !LPropCompareProp( lprowCopy->lpProps
  1032. , (*plprow)->lpProps))
  1033. {
  1034. sc = MAPI_E_INVALID_PARAMETER;
  1035. goto err;
  1036. }
  1037. // Insert it to the end of the unsorted row set
  1038. if ( FAILED(sc = ScAddRow((LPUNKOBJ) lptad,
  1039. NULL, // No sort order
  1040. lprowCopy,
  1041. uliRow,
  1042. &lptad->ulcRowsAdd,
  1043. &lptad->ulcRowMacAdd,
  1044. &lptad->parglprowAdd,
  1045. NULL)) )
  1046. {
  1047. DebugTrace(TEXT("TAD::ScInsertRow() - Error appending new row to TAD (SCODE = 0x%08lX)\n"), sc );
  1048. goto err;
  1049. }
  1050. // Insert the row into the Index sorted row set
  1051. if ( FAILED(sc = ScAddRow((LPUNKOBJ) lptad,
  1052. NULL, // We already collated the row
  1053. lprowCopy,
  1054. (ULONG) (plprow - lptad->parglprowIndex),
  1055. &lptad->ulcRowsIndex,
  1056. &lptad->ulcRowMacIndex,
  1057. &lptad->parglprowIndex,
  1058. NULL)) )
  1059. {
  1060. DebugTrace(TEXT("TAD::ScInsertRow() - Error appending new row to TAD (SCODE = 0x%08lX)\n"), sc );
  1061. goto err;
  1062. }
  1063. UpdateViews(lptad, 0, NULL, 1, &lprowCopy, &lprowCopy);
  1064. ret:
  1065. UnlockObj(lptad);
  1066. return ResultFromScode(sc);
  1067. err:
  1068. // Reset the TAD columns
  1069. lptad->lpptaCols->cValues -= cTagsAdded;
  1070. goto ret;
  1071. }
  1072. /*============================================================================
  1073. - ScCopyTadRowSet()
  1074. -
  1075. * Validates and makes a copy of the specified row set using MAPI memory
  1076. * allocation filtering out PT_ERROR and PT_NULL columns and moving the
  1077. * index column to the first column.
  1078. *
  1079. * if lplpptaNew is not NULL then a list of columns which are new to
  1080. * the TAD are returned.
  1081. *
  1082. *
  1083. * Parameters:
  1084. * lptad in Table data object
  1085. * lprowsetIn in Row set to copy
  1086. * pcNewTags out Number of columns new to the TAD
  1087. * pcRows out Count of rows in the two row sets
  1088. * pparglprowUnsortedCopy out Pointer to copied rows (Unsorted)
  1089. * pparglprowSortedCopy out Pointer to copied rows (Sorted on Index)
  1090. */
  1091. SCODE
  1092. ScCopyTadRowSet(
  1093. LPTAD lptad,
  1094. LPSRowSet lprowsetIn,
  1095. ULONG * pcNewTags,
  1096. ULONG * pcRows,
  1097. LPSRow * * pparglprowUnsortedCopy,
  1098. LPSRow * * pparglprowSortedCopy )
  1099. {
  1100. SCODE sc = S_OK;
  1101. ULONG cRows = 0;
  1102. LPSRow lprowIn;
  1103. LPSRow lprowCopy = NULL;
  1104. ULONG ulcRowsCopy = 0;
  1105. ULONG ulcRowsMacCopy;
  1106. LPSRow FAR * parglprowSortedCopy = NULL;
  1107. LPSRow FAR * parglprowUnsortedCopy = NULL;
  1108. ULONG cNewTags = 0;
  1109. SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 };
  1110. // Assert Itable internal parameters are valid.
  1111. Assert( !pcNewTags
  1112. || !IsBadWritePtr( pcNewTags, sizeof(ULONG)));
  1113. Assert( !IsBadWritePtr( pcRows, sizeof(ULONG)));
  1114. Assert( !IsBadWritePtr( pparglprowUnsortedCopy, sizeof(LPSRow *)));
  1115. Assert( !IsBadWritePtr( pparglprowSortedCopy, sizeof(LPSRow *)));
  1116. // Validate Itable API parameters (ie lprowsetIn)
  1117. // Note! The actual rows will be validated by ScCopyTadRow later.
  1118. if ( IsBadReadPtr( lprowsetIn, sizeof(SRowSet))
  1119. || IsBadReadPtr( lprowsetIn->aRow
  1120. , (UINT) (lprowsetIn->cRows * sizeof(SRow))))
  1121. {
  1122. DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Bad row set In!\n") );
  1123. return MAPI_E_INVALID_PARAMETER;
  1124. }
  1125. // Allocate space the Index sorted list of copied rows
  1126. if ( FAILED(sc = ScAllocateBuffer( lptad,
  1127. sizeof(LPSRow) * lprowsetIn->cRows,
  1128. &parglprowSortedCopy)) )
  1129. {
  1130. DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error allocating parglprowSortedCopy (SCODE = 0x%08lX)\n"), sc );
  1131. goto err;
  1132. }
  1133. MAPISetBufferName(parglprowSortedCopy, TEXT("ITable copied Index sorted row list"));
  1134. // Allocate space the unsorted list of copied rows
  1135. if ( FAILED(sc = ScAllocateBuffer( lptad,
  1136. sizeof(LPSRow) * lprowsetIn->cRows,
  1137. &parglprowUnsortedCopy)) )
  1138. {
  1139. DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error allocating parglprowUnsortedCopy (SCODE = 0x%08lX)\n"), sc );
  1140. goto err;
  1141. }
  1142. MAPISetBufferName(parglprowUnsortedCopy, TEXT("ITable copied unsorted row list"));
  1143. // Set each LPSRow to NULL so we can easily free on error
  1144. ZeroMemory( parglprowSortedCopy, (UINT) (sizeof(LPSRow) * lprowsetIn->cRows));
  1145. ZeroMemory( parglprowUnsortedCopy, (UINT) (sizeof(LPSRow) * lprowsetIn->cRows));
  1146. // Sort the copied rows by IndexCol. The code to add the rows
  1147. // to the TAD relies on this (for speed). This code relies on the sort
  1148. // to find duplicate index columns.
  1149. sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol;
  1150. sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND;
  1151. // Mark our temporary Index Sorted Set as empty.
  1152. ulcRowsCopy = 0;
  1153. ulcRowsMacCopy = lprowsetIn->cRows;
  1154. for ( lprowIn = lprowsetIn->aRow, cRows = ulcRowsMacCopy
  1155. ; cRows
  1156. ; lprowIn++, cRows--)
  1157. {
  1158. LPSRow * plprow;
  1159. ULONG cTagsAdded = 0;
  1160. // Make a copy of the row for our own use filtering
  1161. // out PT_ERROR and PT_NULL columns and moving
  1162. // the index column to the front
  1163. //
  1164. // Also adds any new tags to the TADs column set;
  1165. if ( FAILED(sc = ScCopyTadRow( lptad
  1166. , lprowIn
  1167. , (pcNewTags) ? &cTagsAdded : NULL
  1168. , &lprowCopy)) )
  1169. {
  1170. DebugTrace(TEXT("TAD::HrInsertRow() - Error duping row\n") );
  1171. goto err;
  1172. }
  1173. cNewTags += cTagsAdded;
  1174. // Find out where the row would collate in our Index sorted copy
  1175. // NOTE! We collate before the first occurance so that we will end
  1176. // up pointing at the row with this index if it exists.
  1177. plprow = PlprowCollateRow( ulcRowsCopy
  1178. , parglprowSortedCopy
  1179. , (LPSSortOrderSet) &sosIndex
  1180. , FALSE
  1181. , lprowCopy);
  1182. // If a row with the same Index value exists then we can't insert!
  1183. if ( ulcRowsCopy
  1184. && (plprow < (parglprowSortedCopy + ulcRowsCopy))
  1185. && !LPropCompareProp( lprowCopy->lpProps
  1186. , (*plprow)->lpProps))
  1187. {
  1188. sc = MAPI_E_INVALID_PARAMETER;
  1189. DebugTrace(TEXT("TAD::ScCopyTadRowSet() - The same row index occurred on more than one row.\n") );
  1190. goto err;
  1191. }
  1192. // Append the row to the Unsorted row set
  1193. // This is done before inserting it into the Index Sorted set to make
  1194. // sure ulcRowsCopy is not incremented
  1195. parglprowUnsortedCopy[ulcRowsCopy] = lprowCopy;
  1196. // Insert the row into the Index sorted row set last
  1197. if ( FAILED(sc = ScAddRow( (LPUNKOBJ) lptad
  1198. , NULL // We already collated the row
  1199. , lprowCopy
  1200. , (ULONG) (plprow - parglprowSortedCopy)
  1201. , &ulcRowsCopy
  1202. , &ulcRowsMacCopy
  1203. , &parglprowSortedCopy
  1204. , NULL)) )
  1205. {
  1206. DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error appending new row to RowSet copy (SCODE = 0x%08lX)\n"), sc );
  1207. goto err;
  1208. }
  1209. lprowCopy = NULL;
  1210. }
  1211. *pparglprowUnsortedCopy = parglprowUnsortedCopy;
  1212. *pparglprowSortedCopy = parglprowSortedCopy;
  1213. *pcRows = ulcRowsCopy;
  1214. if (pcNewTags)
  1215. {
  1216. *pcNewTags = cNewTags;
  1217. }
  1218. ret:
  1219. return sc;
  1220. err:
  1221. // Reset the TAD columns
  1222. lptad->lpptaCols->cValues -= cNewTags;
  1223. // We loop through the SORTED row set to free rows because we know that
  1224. // if lprowCopy is not NULL then it hasn't been added to the SORTED set.
  1225. // This prevents a double FreeBuffer on lprowCopy!
  1226. if (parglprowSortedCopy)
  1227. {
  1228. LPSRow * plprowTmp = parglprowSortedCopy;
  1229. while (ulcRowsCopy)
  1230. {
  1231. ScFreeBuffer( lptad, *(plprowTmp++));
  1232. ulcRowsCopy--;
  1233. }
  1234. ScFreeBuffer( lptad, parglprowSortedCopy);
  1235. }
  1236. ScFreeBuffer( lptad, parglprowUnsortedCopy);
  1237. ScFreeBuffer( lptad, lprowCopy);
  1238. goto ret;
  1239. }
  1240. /*============================================================================
  1241. - ScCopyTadRow()
  1242. -
  1243. * Validates and makes a copy of the specified row using MAPI memory
  1244. * allocation filtering out PT_ERROR and PT_NULL columns and moving the
  1245. * index column to the first column.
  1246. *
  1247. * Iff lpcNewTags is not NULL then any new columns are added to the END
  1248. * of TAD's column set and the count of columns added is returned in
  1249. * *lpcNewTags. Tags are added to the end so that the caller can
  1250. * back out changes if necessary.
  1251. *
  1252. * Note! Unlike ScCopyVueROW it is the SRow structure that is
  1253. * allocated and which must be subsequently freed to free the row.
  1254. *
  1255. * Parameters:
  1256. * lptad in Table data object
  1257. * lprow in Row set to copy
  1258. * lpcTagsAdded out Number of columns added to the TAD's col set
  1259. * lplprow out Pointer to copied row
  1260. */
  1261. SCODE
  1262. ScCopyTadRow(
  1263. LPTAD lptad,
  1264. LPSRow lprow,
  1265. ULONG FAR * lpcTagsAdded,
  1266. LPSRow FAR * lplprowCopy )
  1267. {
  1268. ULONG ulcCols = 0;
  1269. LONG iIndexCol;
  1270. CMB cmb;
  1271. SPropValue propTmp;
  1272. LPSPropValue lppropDst;
  1273. LPSPropValue lppropSrc;
  1274. ULONG cTagsAdded = 0;
  1275. LPSRow lprowCopy = NULL;
  1276. SCODE sc = S_OK;
  1277. Assert( !lpcTagsAdded || !IsBadWritePtr( lpcTagsAdded, sizeof(ULONG)));
  1278. Assert( !IsBadWritePtr( lplprowCopy, sizeof(LPSRow)));
  1279. // Validate the input row structure
  1280. if (FBadRow( lprow))
  1281. {
  1282. sc = MAPI_E_INVALID_PARAMETER;
  1283. DebugTrace(TEXT("ScCopyTadRow() - Bad input row\n") );
  1284. return sc;
  1285. }
  1286. // The CMB (CopyMore Buffer) is used so that we do a single MAPI allocation
  1287. // for PropCopyMore to use. It is used in conjuntion with the very
  1288. // special ScBufAllocateMore to keep track of the chunks of memory which
  1289. // would have otherwise been allocated with MAPI - AllocateMore.
  1290. ZeroMemory(&cmb, sizeof(CMB));
  1291. // Figure out how many columns to copy and how much
  1292. // additional memory they'll need to be copied.
  1293. iIndexCol = -1;
  1294. for ( lppropSrc = lprow->lpProps;
  1295. lppropSrc < lprow->lpProps + lprow->cValues;
  1296. lppropSrc++ )
  1297. {
  1298. // Ignore PT_ERROR and PT_NULL properties
  1299. if ( PROP_TYPE(lppropSrc->ulPropTag) == PT_ERROR ||
  1300. PROP_TYPE(lppropSrc->ulPropTag) == PT_NULL )
  1301. continue;
  1302. // If this column is the index column, remember its
  1303. // location in the copied (dst) row
  1304. // so it can be moved to the first column in the copy
  1305. if ( lppropSrc->ulPropTag == lptad->ulPropTagIndexCol )
  1306. iIndexCol = ulcCols;
  1307. // If it's a new property and the caller asked us to (lpcTagsAdded)
  1308. // add the tag to the TAD's column set
  1309. else if ( lpcTagsAdded
  1310. && !FFindColumn( lptad->lpptaCols, lppropSrc->ulPropTag))
  1311. {
  1312. // Realloc the column only if there is no room
  1313. if (lptad->lpptaCols->cValues >= lptad->ulcColsMac)
  1314. {
  1315. sc = ScCOReallocate( lptad
  1316. , CbNewSPropTagArray( lptad->ulcColsMac
  1317. + COLUMN_CHUNK_SIZE)
  1318. , &lptad->lpptaCols);
  1319. if (FAILED(sc))
  1320. {
  1321. DebugTrace(TEXT("TAD::ScCopyTadRow() - Error resizing default column set (SCODE = 0x%08lX)\n"), sc );
  1322. goto err;
  1323. }
  1324. lptad->ulcColsMac += COLUMN_CHUNK_SIZE;
  1325. }
  1326. // Add the column to the end of the existing column set
  1327. lptad->lpptaCols->aulPropTag[lptad->lpptaCols->cValues++]
  1328. = lppropSrc->ulPropTag;
  1329. cTagsAdded++;
  1330. }
  1331. // Add in the size of the column
  1332. cmb.ulcb += UlcbPropToCopy(lppropSrc);
  1333. ++ulcCols;
  1334. }
  1335. // Make sure the row to copy had an index column
  1336. if ( iIndexCol == -1 )
  1337. {
  1338. DebugTrace(TEXT("TAD::ScCopyTadRow() - Row doesn't have an index column!\n") );
  1339. sc = MAPI_E_INVALID_PARAMETER;
  1340. goto err;
  1341. }
  1342. // Allocate space for the entire row (including all allocated values)
  1343. if ( FAILED(sc = ScAllocateBuffer( lptad,
  1344. sizeof(SRow) + 4 + // +4 to start lpProp at 8-byte bndry
  1345. ulcCols * sizeof(SPropValue) +
  1346. cmb.ulcb,
  1347. &lprowCopy)) )
  1348. {
  1349. DebugTrace(TEXT("TAD::ScCopyTadRow() - Error allocating row copy (SCODE = 0x%08lX)\n"), sc );
  1350. goto err;
  1351. }
  1352. MAPISetBufferName(lprowCopy, TEXT("ITable copy of entire row"));
  1353. // Fill in the allocated SRow structure
  1354. // WARNING! The allocate SRow structre MUST always be the first
  1355. // structure in the memory allocation and the property
  1356. // array MUST always immediately follow the SRow structure!
  1357. // Itable code which passes internal rows around counts on
  1358. // this.
  1359. lprowCopy->cValues = ulcCols;
  1360. lprowCopy->lpProps = (LPSPropValue)(((LPBYTE)lprowCopy) + sizeof(SRow)+4);
  1361. // Set the initial pointer in our special CopyMoreBuffer. This buffer
  1362. // will be used by our special AllocateMore routine to allocate
  1363. // strings, bins, etc in PropCopyMore below.
  1364. cmb.lpv = lprowCopy->lpProps + ulcCols;
  1365. // Copy the row properties
  1366. lppropDst = lprowCopy->lpProps + ulcCols;
  1367. lppropSrc = lprow->lpProps + lprow->cValues;
  1368. while ( lppropSrc-- > lprow->lpProps )
  1369. {
  1370. // Strip out properties of type PT_ERROR and PT_NULL
  1371. if ( PROP_TYPE(lppropSrc->ulPropTag) == PT_ERROR
  1372. || PROP_TYPE(lppropSrc->ulPropTag) == PT_NULL )
  1373. {
  1374. continue;
  1375. }
  1376. // Copy the property
  1377. SideAssert( PropCopyMore( --lppropDst
  1378. , lppropSrc
  1379. , (LPALLOCATEMORE) ScBufAllocateMore
  1380. , &cmb) == S_OK );
  1381. }
  1382. // Move the index column to the front
  1383. propTmp = *(lprowCopy->lpProps);
  1384. *(lprowCopy->lpProps) = lprowCopy->lpProps[iIndexCol];
  1385. lprowCopy->lpProps[iIndexCol] = propTmp;
  1386. // Return the copied row and the count of new properties
  1387. *lplprowCopy = lprowCopy;
  1388. if (lpcTagsAdded)
  1389. {
  1390. *lpcTagsAdded = cTagsAdded;
  1391. }
  1392. ret:
  1393. return sc;
  1394. err:
  1395. // Reset the TAD columns
  1396. lptad->lpptaCols->cValues -= cTagsAdded;
  1397. ScFreeBuffer( lptad, lprowCopy);
  1398. goto ret;
  1399. }
  1400. /*============================================================================
  1401. - UpdateViews()
  1402. -
  1403. * Updates all the views on a particular table data. For each view, if
  1404. * lprowToRemove is non-NULL and present in the view, it is removed
  1405. * from the view. If lprowToAdd is non-NULL and satisfies the current
  1406. * restriction on the view, it is added to the view at the position
  1407. * dictated by the view's current sort order.
  1408. *
  1409. * If the row to remove is bookmarked, the bookmark is moved to the next
  1410. * row.
  1411. *
  1412. * OOM errors in adding a row to a view's row list are ignored; the view
  1413. * simply doesn't see the new row.
  1414. *
  1415. *
  1416. * Parameters:
  1417. * lptad in TAD containing views to update
  1418. * cRowsToRemove in Count of rows to remove from each view
  1419. * parglprowToRemove in Array of LPSRows to remove from each view
  1420. * cRowsToAdd in Count of rows to to each view
  1421. * parglprowToAddUnsorted in Unsorted array of LPSRows to add to each view
  1422. * parglprowToAddSorted in Sorted array of LPSRows to add to each view
  1423. */
  1424. VOID
  1425. UpdateViews(
  1426. LPTAD lptad,
  1427. ULONG cRowsToRemove,
  1428. LPSRow * parglprowToRemove,
  1429. ULONG cRowsToAdd,
  1430. LPSRow * parglprowToAddUnsorted,
  1431. LPSRow * parglprowToAddSorted )
  1432. {
  1433. LPVUE lpvue;
  1434. // This is an internal call which assumes that lptad is locked.
  1435. for ( lpvue = (LPVUE) lptad->lpvueList;
  1436. lpvue != NULL;
  1437. lpvue = (LPVUE) lpvue->lpvueNext )
  1438. {
  1439. AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE))
  1440. , TEXT("Bad lpvue in TAD vue List.") );
  1441. FixupView( lpvue
  1442. , cRowsToRemove, parglprowToRemove
  1443. , cRowsToAdd
  1444. , parglprowToAddUnsorted
  1445. , parglprowToAddSorted);
  1446. }
  1447. }
  1448. /*============================================================================
  1449. - FixupView()
  1450. -
  1451. * Updates one view on a particular table data. Each row in
  1452. * parglprowToRemove that is present in the view is removed
  1453. * from the view. Each row in parglprowToAdd that satisfies the current
  1454. * restriction on the view, is added to the view at the position
  1455. * dictated by the view's current sort order.
  1456. *
  1457. * If the row to remove is bookmarked, the bookmark is moved to the next
  1458. * row.
  1459. *
  1460. * OOM errors in adding a row to a view's row list are ignored; the view
  1461. * simply doesn't see the new row.
  1462. *
  1463. *
  1464. * Parameters:
  1465. * lpvue in View to fixup
  1466. * cRowsToRemove in Count of rows to remove from view
  1467. * parglprowToRemove in Array of LPSRows to remove from view
  1468. * cRowsToAdd in Count of rows to to view
  1469. * parglprowToAddUnsorted in Unsorted array of LPSRows to add to view
  1470. * parglprowToAddSorted in Sorted array of LPSRows to add to view
  1471. */
  1472. VOID
  1473. FixupView(
  1474. LPVUE lpvue,
  1475. ULONG cRowsToRemove,
  1476. LPSRow * parglprowToRemove,
  1477. ULONG cRowsToAdd,
  1478. LPSRow * parglprowToAddUnsorted,
  1479. LPSRow * parglprowToAddSorted )
  1480. {
  1481. BOOL fBatchNotifs = TRUE;
  1482. ULONG cNotifs = 0;
  1483. ULONG ulcRows;
  1484. NOTIFICATION argnotifBatch[MAX_BATCHED_NOTIFS];
  1485. SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 };
  1486. LPNOTIFICATION lpnotif;
  1487. PBK pbk;
  1488. SCODE sc;
  1489. Assert( !cRowsToRemove
  1490. || !IsBadReadPtr( parglprowToRemove
  1491. , (UINT) (cRowsToRemove * sizeof(LPSRow))));
  1492. Assert( !cRowsToAdd
  1493. || !IsBadReadPtr( parglprowToAddUnsorted
  1494. , (UINT) (cRowsToAdd * sizeof(LPSRow))));
  1495. Assert( !cRowsToAdd
  1496. || !IsBadReadPtr( parglprowToAddSorted
  1497. , (UINT) (cRowsToAdd * sizeof(LPSRow))));
  1498. if (!cRowsToRemove && !cRowsToAdd)
  1499. {
  1500. // Nothing to do in this case
  1501. goto ret;
  1502. }
  1503. // This is an internal call which assumes that lptad is locked.
  1504. // Set up a an Index sort order so that when we are checking to see
  1505. // if a bookmark is deleted or changed we can search the SORTED row
  1506. // row set using a binary search.
  1507. sosIndex.aSort[0].ulPropTag = lpvue->lptadParent->ulPropTagIndexCol;
  1508. sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND;
  1509. // Mark bookmarks as moving or changed
  1510. // This checks all bookmarks including BOOKMARK_CURRENT and BOOKMARK_END
  1511. // BOOKMARK_BEGINNING is not checked or touched
  1512. // Note that even though BOOKMARK_END is checked, it is not changed.
  1513. pbk = lpvue->rgbk + cBookmarksMax;
  1514. // Rememeber the number of rows in the view so we can fixup bkEnd
  1515. ulcRows = lpvue->bkEnd.uliRow;
  1516. while ( --pbk > lpvue->rgbk )
  1517. {
  1518. LPSRow * plprowBk;
  1519. LPSRow lprowBk;
  1520. ULONG uliRow;
  1521. ULONG fRowReplaced = FALSE;
  1522. // If it's not a valid bookmark, don't update it.
  1523. if ( !(pbk->dwfBKS & dwfBKSValid)
  1524. || (pbk->dwfBKS & dwfBKSStale) )
  1525. {
  1526. continue;
  1527. }
  1528. // Moving bookmarks always point to the actual row
  1529. // Get a row index for moving bookmarks
  1530. if (pbk->dwfBKS & dwfBKSMoving)
  1531. {
  1532. plprowBk = PlprowByLprow( ulcRows
  1533. , lpvue->parglprows
  1534. , pbk->lprow);
  1535. AssertSz( plprowBk
  1536. , TEXT("FixupViews() - Moving Bookmark lost it's row\n"));
  1537. uliRow = (ULONG) (plprowBk - lpvue->parglprows);
  1538. }
  1539. else if (!ulcRows && !pbk->uliRow)
  1540. {
  1541. continue;
  1542. }
  1543. else if ((uliRow = pbk->uliRow) >= ulcRows)
  1544. {
  1545. // Bookmark is at the end of the table. Make sure it stays there
  1546. pbk->uliRow += cRowsToAdd;
  1547. continue;
  1548. }
  1549. Assert(uliRow < ulcRows);
  1550. lprowBk = lpvue->parglprows[uliRow];
  1551. // If a row is on the "Remove" list then it may end up
  1552. // moving or changed depending on whether it is also on the
  1553. // "Add" list.
  1554. if ( cRowsToRemove
  1555. && (plprowBk = PlprowByLprow( cRowsToRemove
  1556. , parglprowToRemove
  1557. , lprowBk)))
  1558. {
  1559. // If a deleted row is on the "Add" list then it is marked as
  1560. // moving and it is pointed (->lprow) at the added row.
  1561. if ( cRowsToAdd
  1562. && (plprowBk = PlprowCollateRow( cRowsToAdd
  1563. , parglprowToAddSorted
  1564. , (LPSSortOrderSet) &sosIndex
  1565. , FALSE
  1566. , lprowBk))
  1567. && (plprowBk < (parglprowToAddSorted + cRowsToAdd))
  1568. && !LPropCompareProp( lprowBk->lpProps
  1569. , (*plprowBk)->lpProps))
  1570. {
  1571. // Row is being replaced
  1572. // Check to see if the row satisfies the specified restriction
  1573. if ( FAILED(sc = ScSatisfiesRestriction( *plprowBk
  1574. , lpvue->lpres
  1575. , &fRowReplaced)) )
  1576. {
  1577. DebugTrace(TEXT("FixupView() - Error evaluating restriction (SCODE = 0x%08lX)\n"), sc );
  1578. goto ret;
  1579. }
  1580. // If it doesn't, return now.
  1581. if ( fRowReplaced )
  1582. {
  1583. pbk->lprow = *plprowBk;
  1584. pbk->dwfBKS = dwfBKSMoving | dwfBKSValid;
  1585. }
  1586. }
  1587. // If a deleted row is not going to be listed then its bookmark
  1588. // is "Changed".
  1589. if (!fRowReplaced)
  1590. {
  1591. // Marked row was deleted.
  1592. pbk->uliRow = uliRow;
  1593. pbk->dwfBKS = dwfBKSChanged | dwfBKSValid;
  1594. }
  1595. }
  1596. // If the row is not on the deletion list then it is automatically
  1597. // marked as moving.
  1598. else
  1599. {
  1600. // Marked row may move
  1601. pbk->lprow = lprowBk;
  1602. pbk->dwfBKS = dwfBKSMoving | dwfBKSValid;
  1603. }
  1604. }
  1605. // Restore bkEnd
  1606. lpvue->bkEnd.uliRow = ulcRows;
  1607. // Remove rows from the view
  1608. for ( ; cRowsToRemove; parglprowToRemove++, cRowsToRemove--)
  1609. {
  1610. LPSRow lprowRemove;
  1611. LPSRow * plprowRemove;
  1612. LPSRow * plprowAdd;
  1613. BOOL fCanReplace = FALSE;
  1614. ULONG uliRowAddDefault;
  1615. // If the REMOVED row is not in the view then there is nothing to do
  1616. if ( !*parglprowToRemove
  1617. || !(plprowRemove = PlprowByLprow( lpvue->bkEnd.uliRow
  1618. , lpvue->parglprows
  1619. , *parglprowToRemove)) )
  1620. {
  1621. continue;
  1622. }
  1623. // We will need this to fill in the notificaton later
  1624. lprowRemove = *plprowRemove;
  1625. // Go ahead and delete the current row from the VUE but remember
  1626. // where it came from (uliRowAddDefault) so that if there is a
  1627. // replacement and no sort order the replacement can be put back
  1628. // into the same place
  1629. uliRowAddDefault = (ULONG) (plprowRemove - lpvue->parglprows);
  1630. MoveMemory( plprowRemove
  1631. , plprowRemove + 1
  1632. , (size_t) (lpvue->bkEnd.uliRow - uliRowAddDefault - 1)
  1633. * sizeof(LPSRow));
  1634. lpvue->bkEnd.uliRow -= 1;
  1635. // See if the deleted row can be replaced by one
  1636. // that is being added. For this to be TRUE:
  1637. // There must be a Row with the same Index in the "Add" list
  1638. // The row in the "Add" list must satisfy the VUE's restriction
  1639. if ( (plprowAdd = PlprowCollateRow( cRowsToAdd
  1640. , parglprowToAddSorted
  1641. , (LPSSortOrderSet) &sosIndex
  1642. , FALSE
  1643. , lprowRemove))
  1644. && (plprowAdd < (parglprowToAddSorted + cRowsToAdd))
  1645. && !LPropCompareProp( (lprowRemove)->lpProps
  1646. , (*plprowAdd)->lpProps) )
  1647. {
  1648. // If the row to add satisifies the current restriction,
  1649. // add it to the view according to the sort order or
  1650. // the default location.
  1651. if ( FAILED(sc = ScMaybeAddRow( lpvue
  1652. , lpvue->lpres
  1653. , lpvue->lpsos
  1654. , *plprowAdd
  1655. , uliRowAddDefault
  1656. , &lpvue->bkEnd.uliRow
  1657. , &lpvue->ulcRowMac
  1658. , &lpvue->parglprows
  1659. , &plprowAdd)) )
  1660. {
  1661. DebugTrace(TEXT("TAD::FixupViews() - Error replacing row in VUE (SCODE = 0x%08lX)\n"), sc );
  1662. goto ret;
  1663. }
  1664. }
  1665. // Make sure that we don't have more than MAX_BATCHED_NOTIFS
  1666. // and that there is no replacment row
  1667. if (!fBatchNotifs || (cNotifs >= MAX_BATCHED_NOTIFS))
  1668. {
  1669. fBatchNotifs = FALSE;
  1670. continue;
  1671. }
  1672. // Init a new notificaton
  1673. lpnotif = argnotifBatch + cNotifs++;
  1674. ZeroMemory(lpnotif, sizeof(NOTIFICATION));
  1675. lpnotif->ulEventType = fnevTableModified;
  1676. // If row was deleted and added back...
  1677. if ( (plprowAdd >= lpvue->parglprows)
  1678. && (plprowAdd < (lpvue->parglprows + lpvue->bkEnd.uliRow)))
  1679. {
  1680. // Fill in a HACKED MODIFIED notificaion
  1681. lpnotif->info.tab.ulTableEvent = TABLE_ROW_MODIFIED;
  1682. // Use the notifs row structure to TEMPORARILY store
  1683. // a pointer to the replacement row.
  1684. lpnotif->info.tab.row.lpProps = (LPSPropValue) (*plprowAdd);
  1685. }
  1686. // ...else row was deleted and NOT added back.
  1687. else
  1688. {
  1689. // Fill in a DELETE notification
  1690. lpnotif->info.tab.ulTableEvent = TABLE_ROW_DELETED;
  1691. lpnotif->info.tab.propIndex = *(lprowRemove->lpProps);
  1692. lpnotif->info.tab.propPrior.ulPropTag = PR_NULL;
  1693. }
  1694. }
  1695. // Add new rows to the table. This is done in the UNSORTED order
  1696. // in case there is no VUE sort order
  1697. for ( ; cRowsToAdd; parglprowToAddUnsorted++, cRowsToAdd--)
  1698. {
  1699. LPSRow * plprowAdd;
  1700. // If the row has already been added then there is nothing to do.
  1701. if ( *parglprowToAddUnsorted
  1702. && (plprowAdd = PlprowByLprow( lpvue->bkEnd.uliRow
  1703. , lpvue->parglprows
  1704. , *parglprowToAddUnsorted)) )
  1705. {
  1706. continue;
  1707. }
  1708. // If the row to add satisifies the current restriction,
  1709. // add it to the view according to the sort order or
  1710. // to the end of the table if no sort order is applied.
  1711. if ( FAILED(sc = ScMaybeAddRow( lpvue
  1712. , lpvue->lpres
  1713. , lpvue->lpsos
  1714. , *parglprowToAddUnsorted
  1715. , lpvue->bkEnd.uliRow
  1716. , &lpvue->bkEnd.uliRow
  1717. , &lpvue->ulcRowMac
  1718. , &lpvue->parglprows
  1719. , &plprowAdd)) )
  1720. {
  1721. DebugTrace(TEXT("TAD::FixupViews() - Error adding row to VUE (SCODE = 0x%08lX)\n"), sc );
  1722. goto ret;
  1723. }
  1724. if (!plprowAdd)
  1725. {
  1726. // Row was not added so don't fill out a notification
  1727. continue;
  1728. }
  1729. // Make sure that we don't have more than MAX_BATCHED_NOTIFS
  1730. // and that there is no replacment row
  1731. if (!fBatchNotifs || (cNotifs >= MAX_BATCHED_NOTIFS))
  1732. {
  1733. fBatchNotifs = FALSE;
  1734. continue;
  1735. }
  1736. // Fill in a HACKED ADDED notificaion
  1737. lpnotif = argnotifBatch + cNotifs++;
  1738. ZeroMemory(lpnotif, sizeof(NOTIFICATION));
  1739. lpnotif->ulEventType = fnevTableModified;
  1740. lpnotif->info.tab.ulTableEvent = TABLE_ROW_ADDED;
  1741. // Use the notifs row structure to TEMPORARILY store
  1742. // a pointer to the replacement row.
  1743. lpnotif->info.tab.row.lpProps = (LPSPropValue) (*plprowAdd);
  1744. }
  1745. // If there too many notifications to batch then fill out a single
  1746. // TABLE_CHANGED notification...
  1747. if (!fBatchNotifs)
  1748. {
  1749. cNotifs = 1;
  1750. lpnotif = argnotifBatch;
  1751. ZeroMemory(lpnotif, sizeof(NOTIFICATION));
  1752. lpnotif->ulEventType = fnevTableModified;
  1753. lpnotif->info.tab.ulTableEvent = TABLE_CHANGED;
  1754. lpnotif->info.tab.propIndex.ulPropTag
  1755. = lpnotif->info.tab.propPrior.ulPropTag
  1756. = PR_NULL;
  1757. }
  1758. // ...else go through the batch of notifications and fixup
  1759. // the ROW_ADDED and ROW_MODIFIED entries.
  1760. else
  1761. {
  1762. LPSRow * plprowNotif;
  1763. // Raid: Horsefly/Exchange/36281
  1764. //
  1765. // The code above which fills in argnotifBatch doesn't necessarily
  1766. // fill in the notifications in an order that can be processed
  1767. // from first to last, which is a requirement for batched
  1768. // notifications. As a workaround, the maximum number of
  1769. // notifications in a batch was changed to 1 (MAX_BATCHED_NOTIFS
  1770. // in _itable.h) so that order is not a problem. Should that
  1771. // ever change to something other than 1, this bug will have to
  1772. // be revisited a well as a crash below where ScFreeBuffer()
  1773. // in the cleanup can end up clobbering a VUE's copy of the row
  1774. // data if ScCopyVueRow() fails. See comment about filling in
  1775. // TEMPORARY pointer to replacement row above.
  1776. //
  1777. AssertSz( cNotifs < 2, TEXT("Batch notifications of more than 1 not supported") );
  1778. for (lpnotif = argnotifBatch + cNotifs; lpnotif-- > argnotifBatch; )
  1779. {
  1780. Assert( (lpnotif->ulEventType == fnevTableModified)
  1781. && ( (lpnotif->info.tab.ulTableEvent == TABLE_ROW_MODIFIED)
  1782. || (lpnotif->info.tab.ulTableEvent == TABLE_ROW_DELETED)
  1783. || (lpnotif->info.tab.ulTableEvent == TABLE_ROW_ADDED)));
  1784. if (lpnotif->info.tab.ulTableEvent == TABLE_ROW_DELETED)
  1785. {
  1786. // DELETE notifications don't need to be fixed up
  1787. continue;
  1788. }
  1789. plprowNotif
  1790. = PlprowByLprow( lpvue->bkEnd.uliRow
  1791. , lpvue->parglprows
  1792. , (LPSRow) (lpnotif->info.tab.row.lpProps));
  1793. Assert(plprowNotif);
  1794. lpnotif->info.tab.propIndex = *((*plprowNotif)->lpProps);
  1795. if (plprowNotif > lpvue->parglprows)
  1796. {
  1797. lpnotif->info.tab.propPrior = *((*(plprowNotif - 1))->lpProps);
  1798. }
  1799. else
  1800. {
  1801. lpnotif->info.tab.propPrior.ulPropTag = PR_NULL;
  1802. }
  1803. // Fill in row added/modified using the column set
  1804. // currently active on the view being notified
  1805. if ( FAILED(sc = ScCopyVueRow( lpvue
  1806. , lpvue->lpptaCols
  1807. , *plprowNotif
  1808. , &(lpnotif->info.tab.row))))
  1809. {
  1810. DebugTrace(TEXT("TAD::UpdateViews() - Error copying row to view notify (SCODE = 0x%08lX)\n"), sc );
  1811. // If the row can't be copied, then just skip this view
  1812. goto ret;
  1813. }
  1814. }
  1815. }
  1816. // If a rows were added, modified or deleted, send the notification
  1817. #ifdef NOTIFICATIONS
  1818. if ( cNotifs )
  1819. {
  1820. VUENOTIFKEY vuenotifkey;
  1821. ULONG ulNotifFlags = 0;
  1822. // Kick off notifications to all the notifications open on the view
  1823. vuenotifkey.ulcb = sizeof(MAPIUID);
  1824. vuenotifkey.mapiuid = lpvue->mapiuidNotif;
  1825. (void) HrNotify((LPNOTIFKEY) &vuenotifkey,
  1826. cNotifs,
  1827. argnotifBatch,
  1828. &ulNotifFlags);
  1829. }
  1830. #endif // NOTIFICATIONS
  1831. ret:
  1832. // Always fixup bkCurrent before leaving
  1833. if ( FBookMarkStale( lpvue, BOOKMARK_CURRENT) )
  1834. {
  1835. TrapSz( TEXT("FixupViews() - BOOKMARK_CURRENT became bad.\n"));
  1836. }
  1837. if (lpvue->bkCurrent.uliRow > lpvue->bkEnd.uliRow)
  1838. {
  1839. lpvue->bkCurrent.uliRow = lpvue->bkEnd.uliRow;
  1840. }
  1841. for (lpnotif = argnotifBatch; cNotifs; lpnotif++, --cNotifs)
  1842. {
  1843. // Free the notification's copy of any added/modified row
  1844. ScFreeBuffer(lpvue, lpnotif->info.tab.row.lpProps);
  1845. }
  1846. return;
  1847. }
  1848. /*============================================================================
  1849. - ScReplaceRows()
  1850. -
  1851. * Replaces the rows with indexes matching the indexes of the list
  1852. * rows with the corresponding row from the list. The old row is then
  1853. * added to the list of replaced (old) rows.
  1854. *
  1855. * If a listed row has no existing counterpart then it is added
  1856. * to TAD's. There is no row added to the replaced row list in this case.
  1857. *
  1858. * If a row is added it is appended to the end of the unsorted row table
  1859. * and collated (by IndexCol) into the Index sorted row table.
  1860. *
  1861. * Parameters:
  1862. * lptad in Table data object
  1863. * cRowsNew in Count of rows to modify/add
  1864. * parglprowNew in List of rows to modify/add
  1865. * pcRowsOld Out Pointer count of rows replaced
  1866. * pparglprowOld out Pointer to list of rows replaced
  1867. */
  1868. SCODE
  1869. ScReplaceRows(
  1870. LPTAD lptad,
  1871. ULONG cRowsNew,
  1872. LPSRow * parglprowNew,
  1873. ULONG * pcRowsOld,
  1874. LPSRow * * pparglprowOld )
  1875. {
  1876. SCODE sc;
  1877. LPSRow * plprowNew;
  1878. LPSRow * plprowOld;
  1879. LPSRow * parglprowOld = NULL;
  1880. // Make sure the table doesn't grow too big. This is not an exact test
  1881. // but will be reasonable in almost all cases. May fail when
  1882. // NumberRowsAdded + NumberRowsDeleted > 64K
  1883. if (HIWORD(lptad->ulcRowsAdd + cRowsNew) != 0)
  1884. {
  1885. sc = MAPI_E_TABLE_TOO_BIG;
  1886. DebugTrace(TEXT("ScReplaceRows() - In memory table has > 32767 rows (SCODE = 0x%08lX)\n"), sc );
  1887. goto ret;
  1888. }
  1889. // Make sure the unsorted and the Index sorted row lists are big
  1890. // enough to handle all of the new rows.
  1891. // This is done first so that we won't fail after we start adding rows.
  1892. if ((lptad->ulcRowsAdd + cRowsNew) >= lptad->ulcRowMacAdd)
  1893. {
  1894. ULONG ulcRowsToAdd;
  1895. ulcRowsToAdd = (cRowsNew > ROW_CHUNK_SIZE) ? cRowsNew
  1896. : ROW_CHUNK_SIZE;
  1897. if (FAILED(sc = ScCOReallocate( lptad
  1898. , (lptad->ulcRowMacAdd + ulcRowsToAdd)
  1899. * sizeof(LPSRow)
  1900. , (VOID **) (&lptad->parglprowAdd))))
  1901. {
  1902. DebugTrace(TEXT("ScAddRows() - Error growing unsorted row set (SCODE = 0x%08lX)\n"), sc );
  1903. goto ret;
  1904. }
  1905. // Increment ulcRowMacAdd only AFTER successfull allocation
  1906. lptad->ulcRowMacAdd += ulcRowsToAdd;
  1907. }
  1908. if ((lptad->ulcRowsIndex + cRowsNew) >= lptad->ulcRowMacIndex)
  1909. {
  1910. ULONG ulcRowsToAdd;
  1911. ulcRowsToAdd = (cRowsNew > ROW_CHUNK_SIZE) ? cRowsNew
  1912. : ROW_CHUNK_SIZE;
  1913. if (FAILED(sc = ScCOReallocate( lptad
  1914. , (lptad->ulcRowMacIndex + ulcRowsToAdd)
  1915. * sizeof(LPSRow)
  1916. , (VOID **) (&lptad->parglprowIndex))))
  1917. {
  1918. DebugTrace(TEXT("ScAddRows() - Error growing Index sorted row set (SCODE = 0x%08lX)\n"), sc );
  1919. goto ret;
  1920. }
  1921. // Increment ulcRowMacIndex only AFTER successfull allocation
  1922. lptad->ulcRowMacIndex += ulcRowsToAdd;
  1923. }
  1924. // Allocate the list of old rows now so we won't fail after we start
  1925. // adding rows.
  1926. if (FAILED(sc = ScAllocateBuffer( lptad
  1927. , cRowsNew * sizeof(LPSRow)
  1928. , &parglprowOld)))
  1929. {
  1930. DebugTrace(TEXT("ScAddRows() - Error creating old row list (SCODE = 0x%08lX)\n"), sc );
  1931. goto ret;
  1932. }
  1933. MAPISetBufferName(parglprowOld, TEXT("ITable old row list (replace)"));
  1934. // This routine is not allowed to fail after this point.
  1935. plprowOld = parglprowOld;
  1936. for (plprowNew = parglprowNew; cRowsNew; plprowNew++, cRowsNew--)
  1937. {
  1938. LPSRow * plprow = NULL;
  1939. sc = ScFindRow(lptad, (*plprowNew)->lpProps, &plprow);
  1940. if (sc == S_OK)
  1941. {
  1942. // Put the old row into the old row set
  1943. *plprowOld = *plprow;
  1944. // Replace the row in the Index sorted set
  1945. *plprow = *plprowNew;
  1946. // Replace the row in the unsorted row set
  1947. if (plprow = PlprowByLprow( lptad->ulcRowsAdd
  1948. , lptad->parglprowAdd
  1949. , *plprowOld))
  1950. {
  1951. *plprow = *plprowNew;
  1952. }
  1953. // If the row was in the Index sorted set it should always be in
  1954. // the unsorted set
  1955. Assert(plprow);
  1956. // Point at the next available slot for old rows
  1957. plprowOld++;
  1958. }
  1959. // ...else didn't find the row.
  1960. else
  1961. {
  1962. // Insert the row into the Index sorted set
  1963. sc = ScAddRow( (LPUNKOBJ) lptad
  1964. , NULL // We already collated the row
  1965. , *plprowNew
  1966. , (plprow) ? (ULONG) (plprow - lptad->parglprowIndex) : 0
  1967. , &lptad->ulcRowsIndex
  1968. , &lptad->ulcRowMacIndex
  1969. , &lptad->parglprowIndex
  1970. , NULL);
  1971. AssertSz1( !FAILED(sc)
  1972. , TEXT("TAD::ScReplaceRows() - Error adding row to Index sorted set (SCODE = 0x%08lX)\n")
  1973. , sc);
  1974. // Add the row to the end of the unsorted set
  1975. Assert( !IsBadWritePtr( lptad->parglprowAdd + lptad->ulcRowsAdd
  1976. , sizeof(*plprowNew)));
  1977. lptad->parglprowAdd[lptad->ulcRowsAdd++] = *plprowNew;
  1978. }
  1979. }
  1980. sc = S_OK; // ignore NOT_FOUND error (or Asserted errors).
  1981. if (plprowOld > parglprowOld)
  1982. {
  1983. *pparglprowOld = parglprowOld;
  1984. *pcRowsOld = (ULONG) (plprowOld - parglprowOld);
  1985. }
  1986. else
  1987. {
  1988. ScFreeBuffer(lptad, parglprowOld);
  1989. *pparglprowOld = NULL;
  1990. *pcRowsOld = 0;
  1991. }
  1992. ret:
  1993. return sc;
  1994. //err:
  1995. // No need to free old rows here since call is not allowed to fail
  1996. // after old row list is allocated!
  1997. }
  1998. /*============================================================================
  1999. - VUE::Release()
  2000. -
  2001. */
  2002. STDMETHODIMP_(ULONG)
  2003. VUE_Release( LPVUE lpvue )
  2004. {
  2005. LPTAD lptadParent;
  2006. ULONG ulcRef;
  2007. #if !defined(NO_VALIDATION)
  2008. if ( BAD_STANDARD_OBJ(lpvue,VUE_,Release,lpVtbl))
  2009. {
  2010. DebugTrace(TEXT("VUE::Release() - Bad parameter passed as VUE object\n") );
  2011. return !0;
  2012. }
  2013. #endif
  2014. lptadParent = lpvue->lptadParent;
  2015. LockObj(lptadParent);
  2016. // If there is an instance left then release it
  2017. ulcRef = lpvue->ulcRef;
  2018. if (ulcRef != 0)
  2019. ulcRef = --lpvue->ulcRef;
  2020. // The object can only be destroyed if there is no instance and no
  2021. // active Advise left.
  2022. //$ We can use lpvue->lpAdviselist if we can depend on HrUnsubscribe
  2023. //$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe
  2024. if ( ulcRef == 0 && !lpvue->ulcAdvise )
  2025. {
  2026. CALLERRELEASE FAR * lpfReleaseCallback = lpvue->lpfReleaseCallback;
  2027. ULONG ulReleaseData = lpvue->ulReleaseData;
  2028. LPVUE * plpvue;
  2029. // Call the release callback. Leave our crit sect before
  2030. // calling back to prevent deadlock.
  2031. if (lpfReleaseCallback)
  2032. {
  2033. UnlockObj(lptadParent);
  2034. lpfReleaseCallback(ulReleaseData,
  2035. (LPTABLEDATA) lptadParent,
  2036. (LPMAPITABLE) lpvue);
  2037. LockObj(lptadParent);
  2038. }
  2039. // Search the linked list of VUEs in our parent TAD for this VUE.
  2040. for ( plpvue = &(lptadParent->lpvueList)
  2041. ; *plpvue
  2042. ; plpvue = &((*plpvue)->lpvueNext))
  2043. {
  2044. if (*plpvue == lpvue)
  2045. break;
  2046. }
  2047. // If this VUE was in the list then UNLINK it and FREE it
  2048. if (*plpvue)
  2049. {
  2050. // Unlink the VUE
  2051. *plpvue = lpvue->lpvueNext;
  2052. // Free resources used by the VUE
  2053. ScFreeBuffer(lpvue, lpvue->lpptaCols);
  2054. ScFreeBuffer(lpvue, lpvue->lpres);
  2055. ScFreeBuffer(lpvue, lpvue->lpsos);
  2056. COFree(lpvue, lpvue->parglprows);
  2057. #ifdef NOTIFICATIONS
  2058. DestroyAdviseList(&lpvue->lpAdviseList);
  2059. #endif // NOTIFICATIONS
  2060. UNKOBJ_Deinit((LPUNKOBJ) lpvue);
  2061. ScFreeBuffer(lpvue, lpvue);
  2062. // Unlock and Release the parent TAD
  2063. // This must be done after ScFreeBuffer sinse *pinst may go
  2064. // away
  2065. UnlockObj(lptadParent);
  2066. UlRelease(lptadParent);
  2067. }
  2068. else
  2069. {
  2070. DebugTrace(TEXT("VUE::Release() - Table VUE not linked to TAD"));
  2071. // Just unlock the parent tad. We will leak an unlinked vue.
  2072. UnlockObj(lptadParent);
  2073. }
  2074. }
  2075. else
  2076. {
  2077. #if DEBUG
  2078. if ( ulcRef == 0 && lpvue->ulcAdvise )
  2079. {
  2080. DebugTrace(TEXT("VUE::Release() - Table VUE still has active Advise"));
  2081. }
  2082. #endif // DEBUG
  2083. UnlockObj(lptadParent);
  2084. }
  2085. return ulcRef;
  2086. }
  2087. /*============================================================================
  2088. - VUE::Advise()
  2089. -
  2090. */
  2091. STDMETHODIMP
  2092. VUE_Advise(
  2093. LPVUE lpvue,
  2094. ULONG ulEventMask,
  2095. LPMAPIADVISESINK lpAdviseSink,
  2096. ULONG FAR * lpulConnection)
  2097. {
  2098. #ifdef NOTIFICATIONS
  2099. SCODE sc;
  2100. VUENOTIFKEY vuenotifkey;
  2101. #endif // NOTIFICATIONS
  2102. #if !defined(NO_VALIDATION)
  2103. VALIDATE_OBJ(lpvue,VUE_,Advise,lpVtbl);
  2104. Validate_IMAPITable_Advise(
  2105. lpvue,
  2106. ulEventMask,
  2107. lpAdviseSink,
  2108. lpulConnection);
  2109. #endif
  2110. #ifdef NOTIFICATIONS
  2111. LockObj(lpvue->lptadParent);
  2112. vuenotifkey.ulcb = sizeof(MAPIUID);
  2113. vuenotifkey.mapiuid = lpvue->mapiuidNotif;
  2114. if ( FAILED(sc = GetScode(HrSubscribe(&lpvue->lpAdviseList,
  2115. (LPNOTIFKEY) &vuenotifkey,
  2116. ulEventMask,
  2117. lpAdviseSink,
  2118. 0,
  2119. lpulConnection))) )
  2120. {
  2121. DebugTrace(TEXT("VUE::Advise() - Error subscribing notification (SCODE = 0x%08lX)\n"), sc );
  2122. goto ret;
  2123. }
  2124. //$ We don't need lpvue->ulcAdvise if we can depend on HrUnsubscribe
  2125. //$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe
  2126. ++lpvue->ulcAdvise;
  2127. ret:
  2128. UnlockObj(lpvue->lptadParent);
  2129. return HrSetLastErrorIds(lpvue, sc, 0);
  2130. #endif // NOTIFICATIONS
  2131. return(ResultFromScode(MAPI_E_NO_SUPPORT));
  2132. }
  2133. /*============================================================================
  2134. - VUE::Unadvise()
  2135. -
  2136. */
  2137. STDMETHODIMP
  2138. VUE_Unadvise(
  2139. LPVUE lpvue,
  2140. ULONG ulConnection)
  2141. {
  2142. SCODE sc = S_OK;
  2143. #if !defined(NO_VALIDATION)
  2144. VALIDATE_OBJ(lpvue,VUE_,Unadvise,lpVtbl);
  2145. Validate_IMAPITable_Unadvise( lpvue, ulConnection );
  2146. #endif
  2147. #ifdef NOTIFICATIONS
  2148. LockObj(lpvue->lptadParent);
  2149. if ( FAILED(sc = GetScode(HrUnsubscribe(&lpvue->lpAdviseList,
  2150. ulConnection))) )
  2151. {
  2152. DebugTrace(TEXT("VUE::Unadvise() - Error unsubscribing notification (SCODE = 0x%08lX)\n"), sc );
  2153. goto ret;
  2154. }
  2155. // Decrement our advise count.
  2156. //$ We don't need lpvue->ulcAdvise if we can depend on HrUnsubscribe
  2157. //$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe
  2158. if (lpvue->ulcAdvise)
  2159. {
  2160. --lpvue->ulcAdvise;
  2161. }
  2162. ret:
  2163. UnlockObj(lpvue->lptadParent);
  2164. return ResultFromScode(sc);
  2165. #endif // NOTIFICATIONS
  2166. return(ResultFromScode(MAPI_E_NO_SUPPORT));
  2167. }
  2168. /*============================================================================
  2169. - VUE::GetStatus()
  2170. -
  2171. * Since TAD based IMAPITables don't do anything asynchronously, this
  2172. * function always reports TBLSTAT_COMPLETE.
  2173. */
  2174. STDMETHODIMP
  2175. VUE_GetStatus(
  2176. LPVUE lpvue,
  2177. ULONG FAR * lpulTableStatus,
  2178. ULONG FAR * lpulTableType )
  2179. {
  2180. #if !defined(NO_VALIDATION)
  2181. VALIDATE_OBJ(lpvue,VUE_,GetStatus,lpVtbl);
  2182. Validate_IMAPITable_GetStatus( lpvue, lpulTableStatus, lpulTableType );
  2183. #endif
  2184. *lpulTableStatus = TBLSTAT_COMPLETE;
  2185. *lpulTableType = lpvue->lptadParent->ulTableType;
  2186. return HrSetLastErrorIds(lpvue, S_OK, 0);
  2187. }
  2188. /*============================================================================
  2189. - VUE::SetColumns()
  2190. -
  2191. * Replaces the current column set with a copy of the specified column set
  2192. * and frees the old column set.
  2193. */
  2194. STDMETHODIMP
  2195. VUE_SetColumns(
  2196. LPVUE lpvue,
  2197. LPSPropTagArray lpptaCols,
  2198. ULONG ulFlags )
  2199. {
  2200. LPSPropTagArray lpptaColsCopy;
  2201. SCODE sc;
  2202. #if !defined(NO_VALIDATION)
  2203. // VALIDATE_OBJ(lpvue,VUE_,SetColumns,lpVtbl);
  2204. // Validate_IMAPITable_SetColumns( lpvue, lpptaCols, ulFlags ); // Commented by YST
  2205. #endif
  2206. LockObj(lpvue->lptadParent);
  2207. // Copy the column set
  2208. if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue
  2209. , CbNewSPropTagArray(lpptaCols->cValues)
  2210. , (LPBYTE) lpptaCols
  2211. , 0
  2212. , (LPBYTE FAR *) &lpptaColsCopy)) )
  2213. {
  2214. DebugTrace(TEXT("VUE::SetColumns() - Error duping column set (SCODE = 0x%08lX)\n"), sc );
  2215. goto ret;
  2216. }
  2217. MAPISetBufferName(lpptaColsCopy, TEXT("ITable: dup column set"));
  2218. // Replace the current column set with the copy and
  2219. // free the old one.
  2220. ScFreeBuffer(lpvue, lpvue->lpptaCols);
  2221. lpvue->lpptaCols = lpptaColsCopy;
  2222. // [PaulHi] 4/5/99 @comment Shouldn't
  2223. // clients of the WAB request ANSI/UNICODE based on property tags in the
  2224. // column array, rather than doing mass conversions?
  2225. // Seems like the fix is to create two versions of column property arrays,
  2226. // an ANSI and Unicode version.
  2227. FixupColsWA(lpvue->lpptaCols, lpvue->bMAPIUnicodeTable);
  2228. ret:
  2229. UnlockObj(lpvue->lptadParent);
  2230. return HrSetLastErrorIds(lpvue, sc, 0);
  2231. }
  2232. /*============================================================================
  2233. - VUE::QueryColumns()
  2234. -
  2235. * Returns a copy of the current column set or the available column set.
  2236. */
  2237. STDMETHODIMP
  2238. VUE_QueryColumns(
  2239. LPVUE lpvue,
  2240. ULONG ulFlags,
  2241. LPSPropTagArray FAR * lplpptaCols )
  2242. {
  2243. LPSPropTagArray lpptaCols;
  2244. SCODE sc;
  2245. #if !defined(NO_VALIDATION)
  2246. VALIDATE_OBJ(lpvue,VUE_,QueryColumns,lpVtbl);
  2247. Validate_IMAPITable_QueryColumns( lpvue, ulFlags, lplpptaCols );
  2248. #endif
  2249. LockObj(lpvue->lptadParent);
  2250. // Figure out which column set to return
  2251. lpptaCols = (ulFlags & TBL_ALL_COLUMNS) ?
  2252. lpvue->lptadParent->lpptaCols :
  2253. lpvue->lpptaCols;
  2254. // Return a copy of it to the caller
  2255. if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue
  2256. , CbNewSPropTagArray(lpptaCols->cValues)
  2257. , (LPBYTE) lpptaCols
  2258. , 0
  2259. , (LPBYTE FAR *) lplpptaCols)) )
  2260. {
  2261. DebugTrace(TEXT("VUE::QueryColumns() - Error copying column set to return (SCODE = 0x%08lX)\n"), sc );
  2262. goto ret;
  2263. }
  2264. MAPISetBufferName(*lplpptaCols, TEXT("ITable: QueryColumns column set"));
  2265. ret:
  2266. UnlockObj(lpvue->lptadParent);
  2267. return HrSetLastErrorIds(lpvue, sc, 0);
  2268. }
  2269. /*============================================================================
  2270. - VUE::GetRowCount()
  2271. -
  2272. * Returns the count of rows in the table.
  2273. */
  2274. STDMETHODIMP
  2275. VUE_GetRowCount(
  2276. LPVUE lpvue,
  2277. ULONG ulFlags,
  2278. ULONG FAR * lpulcRows )
  2279. {
  2280. SCODE sc = S_OK;
  2281. #if !defined(NO_VALIDATION)
  2282. VALIDATE_OBJ(lpvue,VUE_,GetRowCount,lpVtbl);
  2283. Validate_IMAPITable_GetRowCount( lpvue, ulFlags, lpulcRows );
  2284. #endif
  2285. LockObj(lpvue->lptadParent);
  2286. *lpulcRows = lpvue->bkEnd.uliRow;
  2287. UnlockObj(lpvue->lptadParent);
  2288. return HrSetLastErrorIds(lpvue, sc, 0);
  2289. }
  2290. /*============================================================================
  2291. - VUE::SeekRow()
  2292. -
  2293. * Seeks to the specified row in the table.
  2294. */
  2295. STDMETHODIMP
  2296. VUE_SeekRow(
  2297. LPVUE lpvue,
  2298. BOOKMARK bkOrigin,
  2299. LONG lcRowsToSeek,
  2300. LONG FAR * lplcRowsSought )
  2301. {
  2302. LONG lcRowsSought;
  2303. PBK pbk;
  2304. SCODE sc = S_OK;
  2305. #if !defined(NO_VALIDATION)
  2306. VALIDATE_OBJ(lpvue,VUE_,SeekRow,lpVtbl);
  2307. Validate_IMAPITable_SeekRow( lpvue, bkOrigin, lcRowsToSeek, lplcRowsSought );
  2308. if ( FBadBookmark(lpvue,bkOrigin) ||
  2309. (lplcRowsSought && IsBadWritePtr(lplcRowsSought,sizeof(LONG))) )
  2310. {
  2311. DebugTrace(TEXT("VUE::SeekRow() - Bad parameter(s) passed\n") );
  2312. return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0);
  2313. }
  2314. #endif
  2315. LockObj(lpvue->lptadParent);
  2316. // Validate the bookmark and adjust Moving and Changed bookmarks
  2317. if ( FBookMarkStale( lpvue, bkOrigin) )
  2318. {
  2319. sc = MAPI_E_INVALID_BOOKMARK;
  2320. DebugTrace(TEXT("VUE::SeekRow() - Invalid bookmark (SCODE = 0x%08lX)\n"), sc );
  2321. goto ret;
  2322. }
  2323. // Do the seek
  2324. pbk = lpvue->rgbk + bkOrigin;
  2325. lcRowsSought = lcRowsToSeek < 0 ?
  2326. -min((LONG) pbk->uliRow, -lcRowsToSeek) :
  2327. min(lcRowsToSeek, (LONG)(lpvue->bkEnd.uliRow - pbk->uliRow));
  2328. lpvue->bkCurrent.uliRow = pbk->uliRow + lcRowsSought;
  2329. // If caller wants to know how far we sought, fill it in
  2330. if ( lplcRowsSought )
  2331. *lplcRowsSought = lcRowsSought;
  2332. // Warn if the bookmark sought from refered to a different
  2333. // row from the last time it was used
  2334. if ( pbk->dwfBKS & dwfBKSChanged )
  2335. {
  2336. pbk->dwfBKS &= ~dwfBKSChanged; // Warn only once
  2337. sc = MAPI_W_POSITION_CHANGED;
  2338. }
  2339. ret:
  2340. UnlockObj(lpvue->lptadParent);
  2341. return HrSetLastErrorIds(lpvue, sc, 0);
  2342. }
  2343. /*============================================================================
  2344. - VUE::SeekRowApprox()
  2345. -
  2346. * Seeks to the approximate fractional position in the table.
  2347. */
  2348. STDMETHODIMP
  2349. VUE_SeekRowApprox(
  2350. LPVUE lpvue,
  2351. ULONG ulNumerator,
  2352. ULONG ulDenominator )
  2353. {
  2354. SCODE sc = S_OK;
  2355. #if !defined(NO_VALIDATION)
  2356. VALIDATE_OBJ(lpvue,VUE_,SeekRowApprox,lpVtbl);
  2357. Validate_IMAPITable_SeekRowApprox( lpvue, ulNumerator, ulDenominator );
  2358. #endif
  2359. LockObj(lpvue->lptadParent);
  2360. // Any fraction whose numerator is greater than or equal to its
  2361. // denominator should be fixed up to be equivalent to ulcRows/ulcRows.
  2362. // (i.e. Seek to the end of the table). Also, fixup denominator
  2363. // so it's never 0 (which would crash the approximate position
  2364. // calculation).
  2365. if ( ulNumerator >= ulDenominator )
  2366. {
  2367. ulDenominator = UlDenominator(lpvue->bkEnd.uliRow);
  2368. ulNumerator = ulDenominator;
  2369. }
  2370. // Pare the approximate position down to 16-bit accuracy
  2371. // (If someone wants to seek approximate to something that's accurate
  2372. // to more than 1/32768th, tough!)
  2373. while ( HIWORD(ulNumerator) != 0 )
  2374. {
  2375. ulNumerator >>= 1;
  2376. ulDenominator >>= 1;
  2377. }
  2378. // Assert that we have less than a word's worth of rows in the table.
  2379. // (If someone wants > 32767 entries in an *IN MEMORY* table, tough!)
  2380. AssertSz( HIWORD(lpvue->bkEnd.uliRow) == 0,
  2381. TEXT("Table has more than 32767 rows. Can't be supported in memory.") );
  2382. // Set the position
  2383. lpvue->bkCurrent.uliRow = lpvue->bkEnd.uliRow * ulNumerator / ulDenominator;
  2384. UnlockObj(lpvue->lptadParent);
  2385. return HrSetLastErrorIds(lpvue, sc, 0);
  2386. }
  2387. /*============================================================================
  2388. - VUE::QueryPosition()
  2389. -
  2390. * Query the current exact and approximate fractional position in
  2391. * the table.
  2392. */
  2393. STDMETHODIMP
  2394. VUE_QueryPosition(
  2395. LPVUE lpvue,
  2396. ULONG FAR * lpulRow,
  2397. ULONG FAR * lpulNumerator,
  2398. ULONG FAR * lpulDenominator )
  2399. {
  2400. SCODE sc = S_OK;
  2401. #if !defined(NO_VALIDATION)
  2402. VALIDATE_OBJ(lpvue,VUE_,QueryPosition,lpVtbl);
  2403. Validate_IMAPITable_QueryPosition(
  2404. lpvue,
  2405. lpulRow,
  2406. lpulNumerator,
  2407. lpulDenominator);
  2408. #endif
  2409. LockObj(lpvue->lptadParent);
  2410. *lpulRow = lpvue->bkCurrent.uliRow;
  2411. *lpulNumerator = lpvue->bkCurrent.uliRow;
  2412. *lpulDenominator = UlDenominator(lpvue->bkEnd.uliRow);
  2413. UnlockObj(lpvue->lptadParent);
  2414. return HrSetLastErrorIds(lpvue, sc, 0);
  2415. }
  2416. /*============================================================================
  2417. - VUE::FindRow()
  2418. -
  2419. */
  2420. STDMETHODIMP
  2421. VUE_FindRow(
  2422. LPVUE lpvue,
  2423. LPSRestriction lpres,
  2424. BOOKMARK bkOrigin,
  2425. ULONG ulFlags )
  2426. {
  2427. PBK pbk;
  2428. LPSRow * plprow;
  2429. ULONG fSatisfies;
  2430. SCODE sc;
  2431. #if !defined(NO_VALIDATION)
  2432. VALIDATE_OBJ(lpvue,VUE_,FindRow,lpVtbl);
  2433. Validate_IMAPITable_FindRow(
  2434. lpvue,
  2435. lpres,
  2436. bkOrigin,
  2437. ulFlags );
  2438. if ( FBadBookmark(lpvue,bkOrigin) )
  2439. {
  2440. DebugTrace(TEXT("VUE::FindRow() - Bad parameter(s) passed\n") );
  2441. return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0);
  2442. }
  2443. #endif
  2444. LockObj(lpvue->lptadParent);
  2445. // Validate the bookmark and adjust Moving and Changed bookmarks
  2446. if ( FBookMarkStale( lpvue, bkOrigin) )
  2447. {
  2448. sc = MAPI_E_INVALID_BOOKMARK;
  2449. DebugTrace(TEXT("VUE::FindRow() - Invalid bookmark (SCODE = 0x%08lX)\n"), sc );
  2450. goto ret;
  2451. }
  2452. pbk = lpvue->rgbk + bkOrigin;
  2453. plprow = lpvue->parglprows + pbk->uliRow;
  2454. if ( ulFlags & DIR_BACKWARD )
  2455. {
  2456. while ( plprow-- > lpvue->parglprows )
  2457. {
  2458. if ( FAILED(sc = ScSatisfiesRestriction(*plprow,
  2459. lpres,
  2460. &fSatisfies)) )
  2461. {
  2462. DebugTrace(TEXT("VUE::FindRow() - Error evaluating restriction on row (SCODE = 0x%08lX)\n"), sc );
  2463. goto ret;
  2464. }
  2465. if ( fSatisfies )
  2466. goto found_row;
  2467. }
  2468. }
  2469. else
  2470. {
  2471. while ( plprow < lpvue->parglprows + lpvue->bkEnd.uliRow )
  2472. {
  2473. if ( FAILED(sc = ScSatisfiesRestriction(*plprow,
  2474. lpres,
  2475. &fSatisfies )) )
  2476. {
  2477. DebugTrace(TEXT("VUE::FindRow() - Error evaluating restriction on row (SCODE = 0x%08lX)\n"), sc );
  2478. goto ret;
  2479. }
  2480. if ( fSatisfies )
  2481. goto found_row;
  2482. ++plprow;
  2483. }
  2484. }
  2485. sc = MAPI_E_NOT_FOUND;
  2486. goto ret;
  2487. found_row:
  2488. lpvue->bkCurrent.uliRow = (ULONG) (plprow - lpvue->parglprows);
  2489. // Warn if the bookmark sought from refered to a different
  2490. // row from the last time it was used
  2491. if ( pbk->dwfBKS & dwfBKSChanged )
  2492. {
  2493. pbk->dwfBKS &= ~dwfBKSChanged; // Warn only once
  2494. sc = MAPI_W_POSITION_CHANGED;
  2495. }
  2496. ret:
  2497. UnlockObj(lpvue->lptadParent);
  2498. return HrSetLastErrorIds(lpvue, sc, 0);
  2499. }
  2500. /*============================================================================
  2501. - HrVUERestrict()
  2502. -
  2503. * 4/22/97
  2504. * This is basically the VUE_Restrict function isolated so that it can be
  2505. * called from LDAPVUE_Restrict without any parameter validation
  2506. *
  2507. *
  2508. */
  2509. HRESULT HrVUERestrict( LPVUE lpvue,
  2510. LPSRestriction lpres,
  2511. ULONG ulFlags )
  2512. {
  2513. LPSRestriction lpresCopy;
  2514. SCODE sc;
  2515. LockObj(lpvue->lptadParent);
  2516. // Make a copy of the restriction for our use
  2517. if ( FAILED(sc = ScDupRestriction((LPUNKOBJ) lpvue, lpres, &lpresCopy)) )
  2518. {
  2519. DebugTrace(TEXT("VUE::Restrict() - Error duping restriction (SCODE = 0x%08lX)\n"), sc );
  2520. goto ret;
  2521. }
  2522. // Build a new list of rows from the TAD which satisfy the new restriction
  2523. if ( FAILED(sc = ScLoadRows(lpvue->lptadParent->ulcRowsAdd,
  2524. lpvue->lptadParent->parglprowAdd,
  2525. lpvue,
  2526. lpresCopy,
  2527. lpvue->lpsos)) )
  2528. {
  2529. DebugTrace(TEXT("VUE::Restrict() - Error building restricted row set (SCODE = 0x%08lX)\n"), sc );
  2530. ScFreeBuffer(lpvue, lpresCopy);
  2531. goto ret;
  2532. }
  2533. // Replace the old restriction with the new one
  2534. ScFreeBuffer(lpvue, lpvue->lpres);
  2535. lpvue->lpres = lpresCopy;
  2536. ret:
  2537. UnlockObj(lpvue->lptadParent);
  2538. return HrSetLastErrorIds(lpvue, sc, 0);
  2539. }
  2540. /*============================================================================
  2541. - VUE::Restrict()
  2542. -
  2543. * Reloads the view with rows satisfying the new restriction.
  2544. */
  2545. STDMETHODIMP
  2546. VUE_Restrict(
  2547. LPVUE lpvue,
  2548. LPSRestriction lpres,
  2549. ULONG ulFlags )
  2550. {
  2551. #if !defined(NO_VALIDATION)
  2552. VALIDATE_OBJ(lpvue,VUE_,Restrict,lpVtbl);
  2553. Validate_IMAPITable_Restrict(
  2554. lpvue,
  2555. lpres,
  2556. ulFlags );
  2557. #endif
  2558. return HrVUERestrict(lpvue, lpres, ulFlags);
  2559. }
  2560. /*============================================================================
  2561. - VUE::CreateBookmark()
  2562. -
  2563. */
  2564. STDMETHODIMP
  2565. VUE_CreateBookmark(
  2566. LPVUE lpvue,
  2567. BOOKMARK FAR * lpbkPosition )
  2568. {
  2569. PBK pbk;
  2570. SCODE sc = S_OK;
  2571. IDS ids = 0;
  2572. #if !defined(NO_VALIDATION)
  2573. VALIDATE_OBJ(lpvue,VUE_,CreateBookmark,lpVtbl);
  2574. Validate_IMAPITable_CreateBookmark( lpvue, lpbkPosition);
  2575. #endif
  2576. LockObj(lpvue->lptadParent);
  2577. if ( lpvue->bkCurrent.uliRow == lpvue->bkEnd.uliRow )
  2578. {
  2579. // If we're at EOT, just return bkEnd
  2580. *lpbkPosition = (BOOKMARK) BOOKMARK_END;
  2581. }
  2582. else
  2583. {
  2584. // Othewise, look for a free bookmark and return it
  2585. pbk = lpvue->rgbk + cBookmarksMax;
  2586. while ( pbk-- > lpvue->rgbk )
  2587. {
  2588. if ( pbk->dwfBKS == dwfBKSFree )
  2589. {
  2590. pbk->dwfBKS = dwfBKSValid;
  2591. pbk->uliRow = lpvue->bkCurrent.uliRow;
  2592. *lpbkPosition = (BOOKMARK)(pbk - lpvue->rgbk);
  2593. goto ret;
  2594. }
  2595. }
  2596. DebugTrace(TEXT("VUE::CreateBookmark() - Out of bookmarks\n") );
  2597. sc = MAPI_E_UNABLE_TO_COMPLETE;
  2598. #ifdef OLD_STUFF
  2599. ids = IDS_OUT_OF_BOOKMARKS;
  2600. #endif // OLD_STUFF
  2601. }
  2602. ret:
  2603. UnlockObj(lpvue->lptadParent);
  2604. return HrSetLastErrorIds(lpvue, sc, ids);
  2605. }
  2606. /*============================================================================
  2607. - VUE::FreeBookmark()
  2608. -
  2609. */
  2610. STDMETHODIMP
  2611. VUE_FreeBookmark(
  2612. LPVUE lpvue,
  2613. BOOKMARK bkOrigin )
  2614. {
  2615. SCODE sc = S_OK;
  2616. #if !defined(NO_VALIDATION)
  2617. VALIDATE_OBJ(lpvue,VUE_,FreeBookmark,lpVtbl);
  2618. Validate_IMAPITable_FreeBookmark( lpvue, bkOrigin );
  2619. if ( FBadBookmark(lpvue,bkOrigin) )
  2620. {
  2621. DebugTrace(TEXT("VUE::FreeBookmark() - Bad parameter(s) passed\n") );
  2622. return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0);
  2623. }
  2624. #endif
  2625. LockObj(lpvue->lptadParent);
  2626. // Free the bookmark (ignoring predefined ones)
  2627. if ( bkOrigin > cBookmarksReserved )
  2628. lpvue->rgbk[bkOrigin].dwfBKS = dwfBKSFree;
  2629. UnlockObj(lpvue->lptadParent);
  2630. return HrSetLastErrorIds(lpvue, sc, 0);
  2631. }
  2632. /*============================================================================
  2633. - VUE::SortTable()
  2634. -
  2635. * Reloads the view with rows in the new sort order. Note that the
  2636. * sort order may be NULL (since ITABLE.DLL allows creating tables
  2637. * views NULL sort orders).
  2638. *
  2639. * //$??? While being minimal in code size, this approach is somewhat
  2640. * //$??? slow having to reload the table. If it becomes a performance
  2641. * //$??? issue, a sort function can be implemented instead..
  2642. */
  2643. STDMETHODIMP
  2644. VUE_SortTable(
  2645. LPVUE lpvue,
  2646. LPSSortOrderSet lpsos,
  2647. ULONG ulFlags )
  2648. {
  2649. LPSSortOrderSet lpsosCopy = NULL;
  2650. SCODE sc = S_OK;
  2651. #if !defined(NO_VALIDATION)
  2652. VALIDATE_OBJ(lpvue,VUE_,SortTable,lpVtbl);
  2653. Validate_IMAPITable_SortTable(
  2654. lpvue,
  2655. lpsos,
  2656. ulFlags );
  2657. #endif
  2658. if (lpsos && lpsos->cCategories)
  2659. {
  2660. DebugTrace(TEXT("VUE::SortTable() - VUE::SortTable doesn't support categories\n") );
  2661. #ifdef OLD_STUFF
  2662. return HrSetLastErrorIds(lpvue, MAPI_E_TOO_COMPLEX, IDS_CANT_CATEGORIZE);
  2663. #endif // OLD_STUFF
  2664. return HrSetLastErrorIds(lpvue, MAPI_E_TOO_COMPLEX, 0);
  2665. }
  2666. LockObj(lpvue->lptadParent);
  2667. // If the sort order is not empty then dup it...
  2668. // adding the index column as the least significant sort if it's not
  2669. // already there.
  2670. if ( lpsos && lpsos->cSorts )
  2671. {
  2672. LPSSortOrder lpsoIndex;
  2673. UINT iSortIndex;
  2674. ULONG ulTagIndex = lpvue->lptadParent->ulPropTagIndexCol;
  2675. // Look to see if the index column is alread in the sort.
  2676. for ( lpsoIndex = lpsos->aSort, iSortIndex = 0
  2677. ; iSortIndex < lpsos->cSorts
  2678. ; iSortIndex++, lpsoIndex++)
  2679. {
  2680. if ( (lpsoIndex->ulPropTag == ulTagIndex)
  2681. || ( (PROP_ID(lpsoIndex->ulPropTag) == PROP_ID(ulTagIndex))
  2682. && (PROP_TYPE(lpsoIndex->ulPropTag) == PT_UNSPECIFIED)))
  2683. {
  2684. break;
  2685. }
  2686. }
  2687. // Make a copy of the sort order set for our own use
  2688. if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue
  2689. , CbSSortOrderSet(lpsos)
  2690. , (LPBYTE) lpsos
  2691. , (iSortIndex == lpsos->cSorts)
  2692. ? sizeof(SSortOrder) : 0
  2693. , (LPBYTE FAR *) &lpsosCopy)) )
  2694. {
  2695. DebugTrace(TEXT("VUE::SortTable() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc );
  2696. goto ret;
  2697. }
  2698. MAPISetBufferName(lpsosCopy, TEXT("ITable: SortTable SOS copy"));
  2699. if (iSortIndex == lpsos->cSorts)
  2700. {
  2701. lpsosCopy->aSort[iSortIndex].ulPropTag = ulTagIndex;
  2702. lpsosCopy->aSort[iSortIndex].ulOrder = TABLE_SORT_ASCEND;
  2703. lpsosCopy->cSorts++;
  2704. }
  2705. }
  2706. // ...else the lpsos is empty so we are already sorted
  2707. else
  2708. {
  2709. // Put the old lpsos into lpsosCopy so that it will be freed.
  2710. lpsosCopy = lpvue->lpsos;
  2711. lpvue->lpsos = NULL;
  2712. goto ret;
  2713. }
  2714. // Only do the sort if the new sort is NOT a proper subset of the new
  2715. // sort.
  2716. //$ Well... Now that we added the SECRET last sort, this optimization
  2717. //$ is "almost" useless!
  2718. if ( !lpvue->lpsos
  2719. || lpsosCopy->cSorts > lpvue->lpsos->cSorts
  2720. || memcmp( lpvue->lpsos->aSort
  2721. , lpsosCopy->aSort
  2722. , (UINT) (lpsosCopy->cSorts * sizeof(SSortOrder))) )
  2723. {
  2724. // Sort the VUE rows into a new row set
  2725. // NOTE! We use the rows from the existing VUE set in order to
  2726. // take advantage of the fact the restriction is already
  2727. // done.
  2728. if ( FAILED(sc = ScLoadRows(lpvue->bkEnd.uliRow,
  2729. lpvue->parglprows,
  2730. lpvue,
  2731. NULL, // The restriction is already done
  2732. lpsosCopy)) )
  2733. {
  2734. DebugTrace(TEXT("VUE::SortTable() - Building sorted row set (SCODE = 0x%08lX)\n"), sc );
  2735. goto ret;
  2736. }
  2737. }
  2738. // Swap the old lpsos with lpsosCopy
  2739. lpsos = lpvue->lpsos;
  2740. lpvue->lpsos = lpsosCopy;
  2741. lpsosCopy = lpsos;
  2742. ret:
  2743. // Free the left over SOS
  2744. ScFreeBuffer(lpvue, lpsosCopy);
  2745. UnlockObj(lpvue->lptadParent);
  2746. return HrSetLastErrorIds(lpvue, sc, 0);
  2747. }
  2748. /*============================================================================
  2749. - VUE::QuerySortOrder()
  2750. -
  2751. * Returns the current sort order which may be NULL since ITABLE.DLL
  2752. * allows creation of views with NULL sort orders.
  2753. */
  2754. STDMETHODIMP
  2755. VUE_QuerySortOrder(
  2756. LPVUE lpvue,
  2757. LPSSortOrderSet FAR * lplpsos )
  2758. {
  2759. SCODE sc = S_OK;
  2760. #if !defined(NO_VALIDATION)
  2761. VALIDATE_OBJ(lpvue,VUE_,QuerySortOrder,lpVtbl);
  2762. Validate_IMAPITable_QuerySortOrder(
  2763. lpvue,
  2764. lplpsos );
  2765. #endif
  2766. LockObj(lpvue->lptadParent);
  2767. if ( !lpvue->lpsos )
  2768. {
  2769. UINT cb = CbNewSSortOrderSet(0);
  2770. // allocate a sort order set containing zero sort orders
  2771. if ( FAILED(sc = ScAllocateBuffer(lpvue, cb, (LPBYTE *) lplpsos)))
  2772. {
  2773. DebugTrace(TEXT("VUE::QuerySortOrder() - Error allocating SortOrderSet (SCODE = 0x%08lX)\n"), sc );
  2774. goto ret;
  2775. }
  2776. MAPISetBufferName(*lplpsos, TEXT("ITable new sort order set"));
  2777. // zero the sort order set - which sets the number of sort columns to zero
  2778. ZeroMemory(*lplpsos, cb);
  2779. }
  2780. // Make a copy of our sort order set to return to the caller.
  2781. else if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue
  2782. , CbSSortOrderSet(lpvue->lpsos)
  2783. , (LPBYTE) (lpvue->lpsos)
  2784. , 0
  2785. , (LPBYTE FAR *) lplpsos)) )
  2786. {
  2787. DebugTrace(TEXT("VUE::QuerySortOrder() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc );
  2788. goto ret;
  2789. }
  2790. MAPISetBufferName(*lplpsos, TEXT("ITable: QuerySortOrder SOS"));
  2791. ret:
  2792. UnlockObj(lpvue->lptadParent);
  2793. return HrSetLastErrorIds(lpvue, sc, 0);
  2794. }
  2795. /*============================================================================
  2796. - VUE::QueryRows()
  2797. -
  2798. */
  2799. STDMETHODIMP
  2800. VUE_QueryRows(
  2801. LPVUE lpvue,
  2802. LONG lcRows,
  2803. ULONG ulFlags,
  2804. LPSRowSet FAR * lplprows )
  2805. {
  2806. LPSRow * plprowSrc;
  2807. LPSRow lprowDst;
  2808. LPSRowSet lprows = NULL;
  2809. SCODE sc;
  2810. #define ABS(n) ((n) < 0 ? -1*(n) : (n))
  2811. #if !defined(NO_VALIDATION)
  2812. VALIDATE_OBJ(lpvue,VUE_,QueryRows,lpVtbl);
  2813. #ifndef _WIN64 // Need to investigate more, why this is always fail (YST)
  2814. Validate_IMAPITable_QueryRows(
  2815. lpvue,
  2816. lcRows,
  2817. ulFlags,
  2818. lplprows );
  2819. #endif // _WIN64
  2820. #endif
  2821. LockObj(lpvue->lptadParent);
  2822. // If querying backward, seek back as far as needed and read from there
  2823. plprowSrc = lpvue->parglprows + lpvue->bkCurrent.uliRow;
  2824. if ( lcRows < 0 )
  2825. {
  2826. lcRows = -min((LONG)lpvue->bkCurrent.uliRow, -lcRows);
  2827. plprowSrc += lcRows;
  2828. }
  2829. else
  2830. {
  2831. lcRows = min(lcRows, (LONG)(lpvue->bkEnd.uliRow - lpvue->bkCurrent.uliRow));
  2832. }
  2833. // Allocate the row set
  2834. if ( FAILED(sc = ScAllocateBuffer( lpvue,
  2835. CbNewSRowSet(ABS(lcRows)),
  2836. &lprows)) )
  2837. {
  2838. DebugTrace(TEXT("VUE::QueryRows() - Error allocating row set (SCODE = 0x%08lX)\n"), sc );
  2839. goto err;
  2840. }
  2841. MAPISetBufferName(lprows, TEXT("ITable query rows"));
  2842. // Copy the rows
  2843. // Start with a count of zero rows so we end up with the correct number
  2844. // on partial success
  2845. lprows->cRows = 0;
  2846. for ( lprowDst = lprows->aRow;
  2847. lprowDst < lprows->aRow + ABS(lcRows);
  2848. lprowDst++, plprowSrc++ )
  2849. {
  2850. if ( FAILED(sc = ScCopyVueRow(lpvue,
  2851. lpvue->lpptaCols,
  2852. *plprowSrc,
  2853. lprowDst)) )
  2854. {
  2855. DebugTrace(TEXT("VUE::QueryRows() - Error copying row (SCODE = 0x%08lX)\n"), sc );
  2856. goto err;
  2857. }
  2858. lprows->cRows++;
  2859. }
  2860. if ( (lcRows >= 0 && !(ulFlags & TBL_NOADVANCE))
  2861. || (lcRows < 0 && (ulFlags & TBL_NOADVANCE)) )
  2862. {
  2863. lpvue->bkCurrent.uliRow += lcRows;
  2864. }
  2865. *lplprows = lprows;
  2866. ret:
  2867. UnlockObj(lpvue->lptadParent);
  2868. return HrSetLastErrorIds(lpvue, sc, 0);
  2869. err:
  2870. MAPIFreeRows(lpvue, lprows);
  2871. goto ret;
  2872. #undef ABS
  2873. }
  2874. /*============================================================================
  2875. - VUE::Abort()
  2876. -
  2877. */
  2878. STDMETHODIMP
  2879. VUE_Abort( LPVUE lpvue )
  2880. {
  2881. #if !defined(NO_VALIDATION)
  2882. VALIDATE_OBJ(lpvue,VUE_,Abort,lpVtbl);
  2883. Validate_IMAPITable_Abort( lpvue );
  2884. #endif
  2885. return HrSetLastErrorIds(lpvue, S_OK, 0);
  2886. }
  2887. STDMETHODIMP
  2888. VUE_ExpandRow(LPVUE lpvue, ULONG cbIKey, LPBYTE pbIKey,
  2889. ULONG ulRowCount, ULONG ulFlags, LPSRowSet FAR *lppRows,
  2890. ULONG FAR *lpulMoreRows)
  2891. {
  2892. SCODE sc = S_OK;
  2893. #if !defined(NO_VALIDATION)
  2894. VALIDATE_OBJ(lpvue,VUE_,ExpandRow,lpVtbl);
  2895. Validate_IMAPITable_ExpandRow(
  2896. lpvue,
  2897. cbIKey,
  2898. pbIKey,
  2899. ulRowCount,
  2900. ulFlags,
  2901. lppRows,
  2902. lpulMoreRows);
  2903. #endif
  2904. sc = MAPI_E_NO_SUPPORT;
  2905. return HrSetLastErrorIds(lpvue, sc, 0);
  2906. }
  2907. STDMETHODIMP
  2908. VUE_CollapseRow(LPVUE lpvue, ULONG cbIKey, LPBYTE pbIKey,
  2909. ULONG ulFlags, ULONG FAR *lpulRowCount)
  2910. {
  2911. SCODE sc = S_OK;
  2912. #if !defined(NO_VALIDATION)
  2913. VALIDATE_OBJ(lpvue,VUE_,CollapseRow,lpVtbl);
  2914. Validate_IMAPITable_CollapseRow(
  2915. lpvue,
  2916. cbIKey,
  2917. pbIKey,
  2918. ulFlags,
  2919. lpulRowCount);
  2920. #endif
  2921. sc = MAPI_E_NO_SUPPORT;
  2922. return HrSetLastErrorIds(lpvue, sc, 0);
  2923. }
  2924. STDMETHODIMP
  2925. VUE_WaitForCompletion(LPVUE lpvue, ULONG ulFlags, ULONG ulTimeout,
  2926. ULONG FAR *lpulTableStatus)
  2927. {
  2928. SCODE sc = S_OK;
  2929. #if !defined(NO_VALIDATION)
  2930. VALIDATE_OBJ(lpvue,VUE_,WaitForCompletion,lpVtbl);
  2931. Validate_IMAPITable_WaitForCompletion(
  2932. lpvue,
  2933. ulFlags,
  2934. ulTimeout,
  2935. lpulTableStatus);
  2936. #endif
  2937. if (lpulTableStatus)
  2938. *lpulTableStatus = TBLSTAT_COMPLETE;
  2939. return HrSetLastErrorIds(lpvue, sc, 0);
  2940. }
  2941. STDMETHODIMP
  2942. VUE_GetCollapseState(LPVUE lpvue, ULONG ulFlags, ULONG cbInstanceKey, LPBYTE pbInstanceKey,
  2943. ULONG FAR * lpcbCollapseState, LPBYTE FAR * lppbCollapseState)
  2944. {
  2945. SCODE sc = MAPI_E_NO_SUPPORT;
  2946. #if !defined(NO_VALIDATION)
  2947. VALIDATE_OBJ(lpvue,VUE_,GetCollapseState,lpVtbl);
  2948. Validate_IMAPITable_GetCollapseState(
  2949. lpvue,
  2950. ulFlags,
  2951. cbInstanceKey,
  2952. pbInstanceKey,
  2953. lpcbCollapseState,
  2954. lppbCollapseState);
  2955. #endif
  2956. return HrSetLastErrorIds(lpvue, sc, 0);
  2957. }
  2958. STDMETHODIMP
  2959. VUE_SetCollapseState(LPVUE lpvue, ULONG ulFlags, ULONG cbCollapseState,
  2960. LPBYTE pbCollapseState, BOOKMARK FAR * lpbkLocation)
  2961. {
  2962. SCODE sc = MAPI_E_NO_SUPPORT;
  2963. #if !defined(NO_VALIDATION)
  2964. VALIDATE_OBJ(lpvue,VUE_,SetCollapseState,lpVtbl);
  2965. Validate_IMAPITable_SetCollapseState(
  2966. lpvue,
  2967. ulFlags,
  2968. cbCollapseState,
  2969. pbCollapseState,
  2970. lpbkLocation);
  2971. #endif
  2972. return HrSetLastErrorIds(lpvue, sc, 0);
  2973. }
  2974. /*============================================================================
  2975. - ScDeleteAllRows()
  2976. -
  2977. * Deletes all rows from a TAD and its views.
  2978. *
  2979. * Parameters:
  2980. * lptad in TAD to delete all rows from
  2981. */
  2982. SCODE
  2983. ScDeleteAllRows( LPTAD lptad)
  2984. {
  2985. LPSRow * plprow;
  2986. #ifdef NOTIFICATIONS
  2987. LPVUE lpvue;
  2988. #endif // NOTIFICATIONS
  2989. NOTIFICATION notif;
  2990. // Delete all rows from the unsorted set
  2991. for ( plprow = lptad->parglprowAdd + lptad->ulcRowsAdd
  2992. ; --plprow >= lptad->parglprowAdd
  2993. ;)
  2994. {
  2995. Assert( plprow && *plprow);
  2996. ScFreeBuffer( lptad, *plprow);
  2997. }
  2998. // Tell the TAD it has no rows left
  2999. lptad->ulcRowsAdd = lptad->ulcRowsIndex = 0;
  3000. // Set the constant portion of the notification
  3001. ZeroMemory(&notif, sizeof(NOTIFICATION));
  3002. notif.ulEventType = fnevTableModified;
  3003. notif.info.tab.ulTableEvent = TABLE_RELOAD;
  3004. notif.info.tab.propIndex.ulPropTag
  3005. = notif.info.tab.propPrior.ulPropTag
  3006. = PR_NULL;
  3007. #ifdef NOTIFICATIONS
  3008. // Tell each view that it has no rows left
  3009. for ( lpvue = (LPVUE) lptad->lpvueList;
  3010. lpvue != NULL;
  3011. lpvue = (LPVUE) lpvue->lpvueNext )
  3012. {
  3013. VUENOTIFKEY vuenotifkey;
  3014. ULONG ulNotifFlags = 0;
  3015. AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE))
  3016. && lpvue->lpVtbl == &vtblVUE
  3017. , TEXT("Bad lpvue in TAD vue List.") );
  3018. // Reset all of the bookmarks
  3019. // This automagically tells the view that it has no rows.
  3020. ZeroMemory( lpvue->rgbk, cBookmarksMax * sizeof(BK));
  3021. // Initialize the predefined bookmarks
  3022. lpvue->bkBeginning.dwfBKS = dwfBKSValid;
  3023. lpvue->bkCurrent.dwfBKS = dwfBKSValid;
  3024. lpvue->bkEnd.dwfBKS = dwfBKSValid;
  3025. vuenotifkey.ulcb = sizeof(MAPIUID);
  3026. vuenotifkey.mapiuid = lpvue->mapiuidNotif;
  3027. (void) HrNotify((LPNOTIFKEY) &vuenotifkey,
  3028. 1,
  3029. &notif,
  3030. &ulNotifFlags);
  3031. }
  3032. #endif // NOTIFICATIONS
  3033. // Currently this call cannot fail!
  3034. return S_OK;
  3035. }
  3036. /*============================================================================
  3037. - ScLoadRows()
  3038. -
  3039. * Loads a view's row set with rows from the table data according
  3040. * to the specified restriction and sort order and resets all bookmarks.
  3041. *
  3042. * Parameters:
  3043. * lpvue in View whose row set is to be loaded
  3044. * lpres in Restriction on loaded row set
  3045. * lpsos in Sort order of loaded row set
  3046. */
  3047. SCODE
  3048. ScLoadRows(
  3049. ULONG ulcRowsSrc,
  3050. LPSRow * rglprowsSrc,
  3051. LPVUE lpvue,
  3052. LPSRestriction lpres,
  3053. LPSSortOrderSet lpsos )
  3054. {
  3055. LPTAD lptad = (LPTAD) lpvue->lptadParent;
  3056. LPSRow * plprowSrc;
  3057. LPSRow * plprowDst;
  3058. PBK pbk;
  3059. ULONG ulcRows = 0;
  3060. ULONG ulcRowMac = 0;
  3061. LPSRow * parglprows = NULL;
  3062. SCODE sc = S_OK;
  3063. // Iterate through the table data adding to the view any rows
  3064. // which satisfy the specified restriction. Note, the forward
  3065. // iteration is necessary here so that the rows load in order
  3066. // when no sort order set is specified.
  3067. for ( plprowSrc = rglprowsSrc;
  3068. plprowSrc < rglprowsSrc + ulcRowsSrc;
  3069. plprowSrc++ )
  3070. {
  3071. // If the row satisfies the specified restriction, add it to the
  3072. // row set updating bookmarks as necessary.
  3073. if ( FAILED(sc = ScMaybeAddRow(lpvue,
  3074. lpres,
  3075. lpsos,
  3076. *plprowSrc,
  3077. ulcRows,
  3078. &ulcRows,
  3079. &ulcRowMac,
  3080. &parglprows,
  3081. &plprowDst)) )
  3082. {
  3083. DebugTrace(TEXT("VUE::ScLoadRows() - Error adding row to view (SCODE = 0x%08lX)\n"), sc );
  3084. goto ret;
  3085. }
  3086. }
  3087. // Replace the row set
  3088. COFree(lpvue, lpvue->parglprows);
  3089. lpvue->parglprows = parglprows;
  3090. lpvue->ulcRowMac = ulcRowMac;
  3091. lpvue->bkEnd.uliRow = ulcRows;
  3092. // Lose all user-defined bookmarks, and reset BOOKMARK_CURRENT
  3093. // to BOOKMARK_BEGINNING (i.e. 0) (Raid 1331)
  3094. pbk = lpvue->rgbk + cBookmarksMax;
  3095. while ( pbk-- > lpvue->rgbk + cBookmarksReserved )
  3096. if ( pbk->dwfBKS & dwfBKSValid )
  3097. pbk->dwfBKS = dwfBKSValid | dwfBKSStale;
  3098. lpvue->bkCurrent.uliRow = 0;
  3099. ret:
  3100. return sc;
  3101. }
  3102. /*============================================================================
  3103. - ScMaybeAddRow()
  3104. -
  3105. * If the specified row satisfies the specified restriction, add it
  3106. * to the row set of the specified view according to the specified
  3107. * sort order returning a pointer to the location where the row was
  3108. * added.
  3109. *
  3110. *
  3111. * Parameters:
  3112. * lpvue in VUE with instance variable containing
  3113. * allocators.
  3114. * lpres in Restriction row must satisfy to be added
  3115. * lpsos in Sort order of row set.
  3116. * lprow in Row to maybe add.
  3117. * ulcRows in Count of rows in the row set.
  3118. * pparglprows in/out Pointer to buffer containing row set.
  3119. * pplprow out Pointer to location of added row in row set.
  3120. * (Set to NULL if the row isn't added.)
  3121. */
  3122. SCODE
  3123. ScMaybeAddRow(
  3124. LPVUE lpvue,
  3125. LPSRestriction lpres,
  3126. LPSSortOrderSet lpsos,
  3127. LPSRow lprow,
  3128. ULONG uliRow,
  3129. ULONG * pulcRows,
  3130. ULONG * pulcRowMac,
  3131. LPSRow ** pparglprows,
  3132. LPSRow ** pplprow )
  3133. {
  3134. ULONG fSatisfies;
  3135. SCODE sc;
  3136. // Check to see if the row satisfies the specified restriction
  3137. if ( FAILED(sc = ScSatisfiesRestriction(lprow, lpres, &fSatisfies)) )
  3138. {
  3139. DebugTrace(TEXT("VUE::ScMaybeAddRow() - Error evaluating restriction (SCODE = 0x%08lX)\n"), sc );
  3140. return sc;
  3141. }
  3142. // If it doesn't, return now.
  3143. if ( !fSatisfies )
  3144. {
  3145. *pplprow = NULL;
  3146. return S_OK;
  3147. }
  3148. // The row satisfies the restriction, so add it to the row set
  3149. // according to the specified sort order
  3150. if ( FAILED(sc = ScAddRow((LPUNKOBJ) lpvue,
  3151. lpsos,
  3152. lprow,
  3153. uliRow,
  3154. pulcRows,
  3155. pulcRowMac,
  3156. pparglprows,
  3157. pplprow)) )
  3158. {
  3159. DebugTrace(TEXT("VUE::ScMaybeAddRow() - Error adding row (SCODE = 0x%08lX)\n"), sc );
  3160. return sc;
  3161. }
  3162. return S_OK;
  3163. }
  3164. /*============================================================================
  3165. - ScAddRow()
  3166. -
  3167. * Adds a row to a row set according to the specified sort order returning
  3168. * a pointer to the location in the row set where the row was added.
  3169. *
  3170. *
  3171. * Parameters:
  3172. * lpunkobj in UNKOBJ with instance variable containing
  3173. * allocators.
  3174. * lpsos in Sort order of row set.
  3175. * lprow in Row to maybe add.
  3176. * uliRow in Location to add row if lpsos is NULL
  3177. * pulcRows in/out Count of rows in the row set.
  3178. * pparglprows in/out Pointer to buffer containing row set.
  3179. * pplprow out Pointer to location of added row in row set.
  3180. */
  3181. SCODE
  3182. ScAddRow(
  3183. LPUNKOBJ lpunkobj,
  3184. LPSSortOrderSet lpsos,
  3185. LPSRow lprow,
  3186. ULONG uliRow,
  3187. ULONG * pulcRows,
  3188. ULONG * pulcRowMac,
  3189. LPSRow ** pparglprows,
  3190. LPSRow ** pplprow )
  3191. {
  3192. LPSRow * plprow;
  3193. SCODE sc = S_OK;
  3194. Assert(lpsos || uliRow <= *pulcRows);
  3195. if (HIWORD(*pulcRows + 1) != 0)
  3196. {
  3197. sc = MAPI_E_TABLE_TOO_BIG;
  3198. DebugTrace(TEXT("ScAddRow() - In memory table has > 32767 rows (SCODE = 0x%08lX)\n"), sc );
  3199. goto ret;
  3200. }
  3201. // Grow the row set
  3202. if (*pulcRows >= *pulcRowMac)
  3203. {
  3204. sc = ScCOReallocate( lpunkobj
  3205. , (*pulcRowMac + ROW_CHUNK_SIZE) * sizeof(LPSRow)
  3206. , (LPVOID*) pparglprows);
  3207. if (FAILED(sc))
  3208. {
  3209. DebugTrace(TEXT("ScAddRow() - Error growing row set (SCODE = 0x%08lX)\n"), sc );
  3210. goto ret;
  3211. }
  3212. *pulcRowMac += ROW_CHUNK_SIZE;
  3213. }
  3214. // Collate the row
  3215. if ( lpsos )
  3216. {
  3217. plprow = PlprowCollateRow(*pulcRows, *pparglprows, lpsos, TRUE, lprow);
  3218. }
  3219. else
  3220. {
  3221. plprow = *pparglprows + uliRow;
  3222. }
  3223. // And insert it into the row set
  3224. MoveMemory(plprow+1,
  3225. plprow,
  3226. (size_t) (*pulcRows - (plprow - *pparglprows)) * sizeof(LPSRow));
  3227. *plprow = lprow;
  3228. ++*pulcRows;
  3229. if ( pplprow )
  3230. *pplprow = plprow;
  3231. ret:
  3232. return sc;
  3233. }
  3234. /*============================================================================
  3235. * The following functions are generic utility functions to manipulate
  3236. * table-related data structures. They can easily be modified to be usable
  3237. * in the common subsystem, and should eventually be put there. The reason
  3238. * they are here now is to avoid unnecessarily bloating proputil.c until
  3239. * it can become a lib or DLL.
  3240. */
  3241. /*============================================================================
  3242. - ScCopyVueRow()
  3243. -
  3244. * For use with IMAPITable::QueryRows(). Copies a row by filling in
  3245. * the specified SRow with the count of columns in the row and copying,
  3246. * in order, the specified columns for that row (filling in PT_ERROR
  3247. * for columns with no value in the row) into a prop value array
  3248. * allocated using MAPI linked memory.
  3249. *
  3250. *
  3251. * Parameters:
  3252. * lpunkobj in UNKOBJ with instance variable containing
  3253. * MAPI allocators.
  3254. * lpptaCols in Columns to copy.
  3255. * lprowSrc in Row to copy.
  3256. * lprowDst out Copied row.
  3257. */
  3258. SCODE
  3259. ScCopyVueRow(
  3260. LPVUE lpvue,
  3261. LPSPropTagArray lpptaCols,
  3262. LPSRow lprowSrc,
  3263. LPSRow lprowDst )
  3264. {
  3265. ULONG ulcCols = lpptaCols->cValues;
  3266. ULONG * pulPropTag;
  3267. LPSPropValue lppropSrc;
  3268. LPSPropValue lppropDst;
  3269. CMB cmb;
  3270. SCODE sc;
  3271. ZeroMemory(&cmb, sizeof(CMB));
  3272. // Calculate space needed to copy the requested columns
  3273. pulPropTag = (ULONG *) (lpptaCols->aulPropTag + ulcCols);
  3274. while ( pulPropTag-- > lpptaCols->aulPropTag )
  3275. {
  3276. lppropSrc = lprowSrc->lpProps + lprowSrc->cValues;
  3277. while ( lppropSrc-- > lprowSrc->lpProps )
  3278. if ( lppropSrc->ulPropTag == *pulPropTag )
  3279. {
  3280. cmb.ulcb += UlcbPropToCopy(lppropSrc);
  3281. break;
  3282. }
  3283. }
  3284. // Initialize the pointer to NULL in case the allocation fails
  3285. lprowDst->lpProps = NULL;
  3286. // Allocate the prop value array for those columns
  3287. if ( FAILED(sc = ScAllocateBuffer( lpvue,
  3288. cmb.ulcb + ulcCols * sizeof(SPropValue),
  3289. &lprowDst->lpProps)) )
  3290. {
  3291. DebugTrace(TEXT("ScCopyRow() - Error allocating row copy (SCODE = 0x%08lX)\n"), sc );
  3292. return sc;
  3293. }
  3294. MAPISetBufferName(lprowDst->lpProps, TEXT("ITable: one row"));
  3295. lprowDst->cValues = ulcCols;
  3296. cmb.lpv = lprowDst->lpProps + ulcCols;
  3297. // Copy the columns
  3298. pulPropTag = (ULONG *) (lpptaCols->aulPropTag + ulcCols);
  3299. lppropDst = lprowDst->lpProps + ulcCols;
  3300. while ( --pulPropTag, lppropDst-- > lprowDst->lpProps )
  3301. {
  3302. // Find the column in the source row
  3303. lppropSrc = lprowSrc->lpProps + lprowSrc->cValues;
  3304. while ( lppropSrc-- > lprowSrc->lpProps )
  3305. if ( lppropSrc->ulPropTag == *pulPropTag )
  3306. {
  3307. // Copy it into the dest row
  3308. SideAssert( PropCopyMore(lppropDst,
  3309. lppropSrc,
  3310. (LPALLOCATEMORE) ScBufAllocateMore,
  3311. &cmb) == S_OK );
  3312. goto next_column;
  3313. }
  3314. // No corresponding column -->
  3315. // Copy a null property for this column
  3316. //
  3317. // Yeah, we want to do this, but we don't want to do this
  3318. // on PR_NULL properties!
  3319. //
  3320. if (*pulPropTag != PR_NULL)
  3321. {
  3322. lppropDst->ulPropTag = PROP_TAG(PT_ERROR,PROP_ID(*pulPropTag));
  3323. lppropDst->Value.err = MAPI_E_NOT_FOUND;
  3324. }
  3325. else
  3326. lppropDst->ulPropTag = PR_NULL;
  3327. next_column:
  3328. ;
  3329. }
  3330. return S_OK;
  3331. }
  3332. /*============================================================================
  3333. - PlprowByLprow()
  3334. -
  3335. * Returns a pointer to where the specified row is in the given row
  3336. * set. Note! This simply compares pointers to rows for equality.
  3337. *
  3338. *
  3339. * Parameters:
  3340. * ulcRows in Count of rows in row set.
  3341. * rglprows in Row set.
  3342. * lprow in Row to collate.
  3343. */
  3344. LPSRow *
  3345. PlprowByLprow( ULONG ulcRows,
  3346. LPSRow * rglprows,
  3347. LPSRow lprow )
  3348. {
  3349. LPSRow * plprow = NULL;
  3350. for (plprow = rglprows; ulcRows ; ulcRows--, plprow++ )
  3351. {
  3352. if (*plprow == lprow)
  3353. return plprow;
  3354. }
  3355. return NULL;
  3356. }
  3357. /*============================================================================
  3358. - PlprowCollateRow()
  3359. -
  3360. * Returns a pointer to where the specified row collates in the
  3361. * specified row set according to the specified sort order set.
  3362. * A NULL sort order set implies collating at the end of the row set.
  3363. *
  3364. *
  3365. * Parameters:
  3366. * ulcRows in Count of rows in row set.
  3367. * rglprows in Row set.
  3368. * lpsos in Sort order to collate on.
  3369. * fAfterExisting in True if the row is to go after a range of
  3370. * equal rows. False means before the range.
  3371. * lprow in Row to collate.
  3372. */
  3373. LPSRow *
  3374. PlprowCollateRow(
  3375. ULONG ulcRows,
  3376. LPSRow * rglprows,
  3377. LPSSortOrderSet lpsos,
  3378. BOOL fAfterExisting,
  3379. LPSRow lprow )
  3380. {
  3381. LPSRow * plprowMic = rglprows;
  3382. LPSRow * plprowMac = rglprows + ulcRows;
  3383. LPSRow * plprow;
  3384. LPSSortOrder lpso;
  3385. LPSPropValue lpprop1;
  3386. LPSPropValue lpprop2;
  3387. LONG lResult;
  3388. ULONG i = 0;
  3389. // If no sort order, collate at the end
  3390. if ( !lpsos )
  3391. return rglprows + ulcRows;
  3392. // Otherwise, collate the row according to the specified sort order
  3393. // using a binary search through the rows
  3394. // Start by checking the last row. This is to speed up the case where
  3395. // the rows added are already in sort order.
  3396. plprow = plprowMac - 1;
  3397. while ( plprowMic < plprowMac )
  3398. {
  3399. lResult = 0;
  3400. lpso = lpsos->aSort;
  3401. for (i=0;!lResult && i<lpsos->cSorts;i++)
  3402. {
  3403. lpprop1 = LpSPropValueFindColumn(lprow, lpso[i].ulPropTag);
  3404. lpprop2 = LpSPropValueFindColumn(*plprow, lpso[i].ulPropTag);
  3405. if ( lpprop1 && lpprop2 )
  3406. {
  3407. // If both the row to be collated and the row being
  3408. // checked against have values for this sort column,
  3409. // compare the two to determine their relative positions
  3410. lResult = LPropCompareProp(lpprop1, lpprop2);
  3411. }
  3412. else
  3413. {
  3414. // Either one or both rows don't have values for this
  3415. // sort column, so the relative position is determined
  3416. // by which row (if any) does have a value.
  3417. lResult = (LONG) (lpprop2 - lpprop1);
  3418. }
  3419. // If sorting in reverse order, flip the sense of the comparison
  3420. if ( lpso[i].ulOrder == TABLE_SORT_DESCEND )
  3421. lResult = -lResult;
  3422. }
  3423. if ( (lResult > 0) || (!lResult && fAfterExisting) )
  3424. {
  3425. // Row collates after this row
  3426. plprowMic = plprow + 1;
  3427. }
  3428. else
  3429. {
  3430. // Row collates before this row
  3431. plprowMac = plprow;
  3432. }
  3433. plprow = (plprowMac - plprowMic) / 2 + plprowMic;
  3434. }
  3435. return plprowMic;
  3436. }
  3437. /*============================================================================
  3438. - UNKOBJ_ScDupRestriction()
  3439. -
  3440. * For use with IMAPITable::Restrict(). Makes a copy of the specified
  3441. * restriction using MAPI linked memory. NULL restrictions are
  3442. * copied trivially.
  3443. *
  3444. * //$BUG ScDupRestriction() calls ScDupRestrictionMore() which is
  3445. * //$BUG recursive.
  3446. *
  3447. *
  3448. * Parameters:
  3449. * lpunkobj in UNKOBJ with instance variable containing
  3450. * MAPI allocators.
  3451. * lpres in Restriction to copy.
  3452. * lplpresCopy out Pointer to copied restriction.
  3453. */
  3454. SCODE
  3455. ScDupRestriction(
  3456. LPUNKOBJ lpunkobj,
  3457. LPSRestriction lpres,
  3458. LPSRestriction FAR * lplpresCopy )
  3459. {
  3460. LPSRestriction lpresCopy;
  3461. SCODE sc = S_OK;
  3462. // Duping a NULL restriction is easy....
  3463. if ( !lpres )
  3464. {
  3465. *lplpresCopy = NULL;
  3466. goto ret;
  3467. }
  3468. // Allocate space for a more complicated restriction
  3469. if ( FAILED(sc = ScAllocateBuffer( lpunkobj,
  3470. sizeof(SRestriction),
  3471. &lpresCopy)) )
  3472. {
  3473. DebugTrace(TEXT("UNKOBJ::ScDupRestriction() - Error allocating restriction copy (SCODE = 0x%08lX)\n"), sc );
  3474. goto ret;
  3475. }
  3476. MAPISetBufferName(lpresCopy, TEXT("ITable: copy of restriction"));
  3477. // And copy it.
  3478. if ( FAILED(sc = ScDupRestrictionMore( lpunkobj,
  3479. lpres,
  3480. lpresCopy,
  3481. lpresCopy )) )
  3482. {
  3483. DebugTrace(TEXT("UNKOBJ_ScDupRestriction() - Error duping complex restriction (SCODE = 0x%08lX)\n"), sc );
  3484. ScFreeBuffer(lpunkobj, lpresCopy);
  3485. goto ret;
  3486. }
  3487. *lplpresCopy = lpresCopy;
  3488. ret:
  3489. return sc;
  3490. }
  3491. /*============================================================================
  3492. - ScDupRestrictionMore()
  3493. -
  3494. * For use with IMAPITable::Restrict(). Makes a copy of the specified
  3495. * restriction using MAPI linked memory.
  3496. *
  3497. * //$BUG ScDupRestrictionMore() is recursive.
  3498. *
  3499. *
  3500. * Parameters:
  3501. * lpunkobj in UNKOBJ with instance variable containing
  3502. * MAPI allocators.
  3503. * lpresSrc in Restriction to copy.
  3504. * lpvLink in Pointer to buffer to which copied restriction
  3505. * should be linked.
  3506. * lplpresDst out Pointer to copied restriction.
  3507. */
  3508. SCODE
  3509. ScDupRestrictionMore(
  3510. LPUNKOBJ lpunkobj,
  3511. LPSRestriction lpresSrc,
  3512. LPVOID lpvLink,
  3513. LPSRestriction lpresDst )
  3514. {
  3515. SCODE sc = S_OK;
  3516. switch ( lpresDst->rt = lpresSrc->rt )
  3517. {
  3518. // 'AND' restrictions and 'OR' restrictions have
  3519. // similar structures, so they can share code
  3520. // to copy them.
  3521. //
  3522. // 'SUB' is about the same as well, only where 'OR' and 'AND'
  3523. // have a count, it has a subobject. The copy works fine if
  3524. // you use a count of 1 for the copy, since the "cRes" member
  3525. // of 'AND' and 'OR' is the same size as the "ulSubObject"
  3526. // member of 'SUBRESTRICTION'.
  3527. case RES_AND:
  3528. case RES_OR:
  3529. case RES_SUBRESTRICTION:
  3530. {
  3531. LONG liRes;
  3532. lpresDst->res.resAnd.cRes = liRes = lpresSrc->res.resAnd.cRes;
  3533. if (lpresDst->rt == RES_SUBRESTRICTION)
  3534. liRes = 1;
  3535. if ( FAILED(sc = ScAllocateMore(
  3536. lpunkobj,
  3537. liRes * sizeof(SRestriction),
  3538. lpvLink,
  3539. &lpresDst->res.resAnd.lpRes)) )
  3540. {
  3541. DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'AND' or 'OR' restriction list (SCODE = 0x%08lX)\n"), sc );
  3542. goto ret;
  3543. }
  3544. while ( --liRes >= 0 )
  3545. {
  3546. if ( FAILED(sc = ScDupRestrictionMore(
  3547. lpunkobj,
  3548. lpresSrc->res.resAnd.lpRes + liRes,
  3549. lpvLink,
  3550. lpresDst->res.resAnd.lpRes + liRes)) )
  3551. {
  3552. DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'AND' or 'OR' restriction (SCODE = 0x%08lX)\n"), sc );
  3553. goto ret;
  3554. }
  3555. }
  3556. goto ret;
  3557. }
  3558. case RES_NOT:
  3559. case RES_COMMENT:
  3560. {
  3561. ULONG cValues;
  3562. // Assert that we can use common code to DUP restriction.
  3563. Assert( offsetof(SCommentRestriction, lpRes)
  3564. == offsetof(SNotRestriction, lpRes));
  3565. if (FAILED(sc = ScAllocateMore(
  3566. lpunkobj,
  3567. sizeof(SRestriction),
  3568. lpvLink,
  3569. &lpresDst->res.resComment.lpRes)))
  3570. {
  3571. DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc );
  3572. goto ret;
  3573. }
  3574. if ( FAILED(sc = ScDupRestrictionMore(
  3575. lpunkobj,
  3576. lpresSrc->res.resComment.lpRes,
  3577. lpvLink,
  3578. lpresDst->res.resComment.lpRes)) )
  3579. {
  3580. DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc );
  3581. goto ret;
  3582. }
  3583. // Dup the Prop Value array for COMMENT restrictions
  3584. if (lpresDst->rt == RES_COMMENT)
  3585. {
  3586. lpresDst->res.resComment.cValues =
  3587. lpresSrc->res.resComment.cValues;
  3588. if ( FAILED(sc = ScAllocateMore(
  3589. lpunkobj,
  3590. sizeof(SPropValue)*
  3591. lpresSrc->res.resComment.cValues,
  3592. lpvLink,
  3593. &lpresDst->res.resComment.lpProp)) )
  3594. {
  3595. DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'COMMENT' property (SCODE = 0x%08lX)\n"), sc );
  3596. goto ret;
  3597. }
  3598. for(cValues=0;cValues<lpresSrc->res.resComment.cValues;cValues++)
  3599. {
  3600. if ( FAILED(sc = PropCopyMore(
  3601. lpresDst->res.resComment.lpProp + cValues,
  3602. lpresSrc->res.resComment.lpProp + cValues,
  3603. lpunkobj->pinst->lpfAllocateMore,
  3604. lpvLink )) )
  3605. {
  3606. DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'COMMENT' restriction prop val (SCODE = 0x%08lX)\n"), sc );
  3607. goto ret;
  3608. }
  3609. }
  3610. }
  3611. goto ret;
  3612. }
  3613. // 'CONTENT' and 'PROPERTY' restrictions have
  3614. // similar structures, so they can share code
  3615. // to copy them
  3616. case RES_CONTENT:
  3617. case RES_PROPERTY:
  3618. {
  3619. lpresDst->res.resContent.ulFuzzyLevel = lpresSrc->res.resContent.ulFuzzyLevel;
  3620. lpresDst->res.resContent.ulPropTag = lpresSrc->res.resContent.ulPropTag;
  3621. if ( FAILED(sc = ScAllocateMore(
  3622. lpunkobj,
  3623. sizeof(SPropValue),
  3624. lpvLink,
  3625. &lpresDst->res.resContent.lpProp)) )
  3626. {
  3627. DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'CONTENT' or 'PROPERTY' property (SCODE = 0x%08lX)\n"), sc );
  3628. goto ret;
  3629. }
  3630. if ( FAILED(sc = PropCopyMore(
  3631. lpresDst->res.resContent.lpProp,
  3632. lpresSrc->res.resContent.lpProp,
  3633. lpunkobj->pinst->lpfAllocateMore,
  3634. lpvLink )) )
  3635. {
  3636. DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'CONTENT' or 'PROPERTY' restriction prop val (SCODE = 0x%08lX)\n"), sc );
  3637. goto ret;
  3638. }
  3639. goto ret;
  3640. }
  3641. // Each of these restrictions contain no pointers in their
  3642. // structures, so they can be copied all the same way
  3643. // by copying the SRestriction union itself
  3644. case RES_COMPAREPROPS:
  3645. case RES_BITMASK:
  3646. case RES_SIZE:
  3647. case RES_EXIST:
  3648. {
  3649. *lpresDst = *lpresSrc;
  3650. goto ret;
  3651. }
  3652. default:
  3653. {
  3654. TrapSz( TEXT("ITABLE:ScDupRestrictionMore - Bad restriction type"));
  3655. }
  3656. }
  3657. ret:
  3658. return sc;
  3659. }
  3660. /*============================================================================
  3661. - ScDupRgbEx()
  3662. -
  3663. * General utility for copying a hunk of bytes using MAPI allocated
  3664. * memory. Useful in IMAPITable::SetColumns/QueryColumns to copy
  3665. * column sets and in IMAPITable::SortTable/QuerySortOrder to copy
  3666. * sort orders.
  3667. *
  3668. *
  3669. * Parameters:
  3670. * lpunkobj in UNKOBJ with instance variable containing
  3671. * MAPI allocators.
  3672. * ulcb in Count of bytes to copy.
  3673. * lpb in Buffer to copy from.
  3674. * lplpbCopy out Pointer to allocated buffer copied to.
  3675. */
  3676. SCODE
  3677. ScDupRgbEx(
  3678. LPUNKOBJ lpunkobj,
  3679. ULONG ulcb,
  3680. LPBYTE lpb,
  3681. ULONG ulcbExtra,
  3682. LPBYTE FAR * lplpbCopy )
  3683. {
  3684. SCODE sc = S_OK;
  3685. if ( FAILED(sc = ScAllocateBuffer(lpunkobj, ulcb + ulcbExtra, lplpbCopy)) )
  3686. {
  3687. goto ret;
  3688. }
  3689. CopyMemory(*lplpbCopy, lpb, (size_t) ulcb);
  3690. ret:
  3691. return sc;
  3692. }
  3693. /*============================================================================
  3694. - ScSatisfiesRestriction()
  3695. -
  3696. * Determines if the specified row satisfies the specified restriction
  3697. *
  3698. * //$BUG This function is recursive....
  3699. *
  3700. *
  3701. * Paremeters:
  3702. * lprow in Row to check.
  3703. * lpres in Restriction to check against.
  3704. * pfSatisfies out Pointer to bool which is the result of the
  3705. * test; TRUE if the row satisfies the
  3706. * restriction, FALSE otherwise.
  3707. */
  3708. SCODE
  3709. ScSatisfiesRestriction(
  3710. LPSRow lprow,
  3711. LPSRestriction lpres,
  3712. ULONG * pfSatisfies )
  3713. {
  3714. SCODE sc = S_OK;
  3715. // Empty restrictions are trivial....
  3716. if ( !lpres )
  3717. {
  3718. *pfSatisfies = TRUE;
  3719. goto ret;
  3720. }
  3721. *pfSatisfies = FALSE;
  3722. switch ( lpres->rt )
  3723. {
  3724. case RES_AND:
  3725. {
  3726. ULONG uliRes;
  3727. for ( uliRes = 0;
  3728. uliRes < lpres->res.resAnd.cRes;
  3729. uliRes++ )
  3730. {
  3731. if ( FAILED(sc = ScSatisfiesRestriction(
  3732. lprow,
  3733. lpres->res.resAnd.lpRes + uliRes,
  3734. pfSatisfies)) )
  3735. {
  3736. DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaluating 'AND' restriction (SCODE = 0x%08lX)\n"), sc );
  3737. goto ret;
  3738. }
  3739. if ( !*pfSatisfies )
  3740. break;
  3741. }
  3742. goto ret;
  3743. }
  3744. case RES_OR:
  3745. {
  3746. ULONG uliRes;
  3747. for ( uliRes = 0;
  3748. uliRes < lpres->res.resOr.cRes;
  3749. uliRes++ )
  3750. {
  3751. if ( FAILED(sc = ScSatisfiesRestriction(
  3752. lprow,
  3753. lpres->res.resOr.lpRes + uliRes,
  3754. pfSatisfies)) )
  3755. {
  3756. DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaluating 'OR' restriction (SCODE = 0x%08lX)\n"), sc );
  3757. goto ret;
  3758. }
  3759. if ( *pfSatisfies )
  3760. break;
  3761. }
  3762. goto ret;
  3763. }
  3764. case RES_COMMENT:
  3765. case RES_NOT:
  3766. {
  3767. // Assert that we can use common code to eval restriction.
  3768. Assert( offsetof(SCommentRestriction, lpRes)
  3769. == offsetof(SNotRestriction, lpRes));
  3770. if ( FAILED(sc = ScSatisfiesRestriction(
  3771. lprow,
  3772. lpres->res.resNot.lpRes,
  3773. pfSatisfies)) )
  3774. {
  3775. DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaulating 'NOT'or 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc );
  3776. goto ret;
  3777. }
  3778. if (lpres->rt == RES_NOT)
  3779. {
  3780. *pfSatisfies = !*pfSatisfies;
  3781. }
  3782. goto ret;
  3783. }
  3784. case RES_CONTENT:
  3785. {
  3786. LPSPropValue lpprop;
  3787. lpprop = LpSPropValueFindColumn(lprow, lpres->res.resContent.ulPropTag);
  3788. *pfSatisfies = lpprop ?
  3789. FPropContainsProp(
  3790. lpprop,
  3791. lpres->res.resContent.lpProp,
  3792. lpres->res.resContent.ulFuzzyLevel) :
  3793. FALSE;
  3794. goto ret;
  3795. }
  3796. case RES_PROPERTY:
  3797. {
  3798. LPSPropValue lpprop;
  3799. *pfSatisfies = FALSE;
  3800. // Special case for PR_ANR
  3801. if(lpres->res.resProperty.ulPropTag == PR_ANR_A || lpres->res.resProperty.ulPropTag == PR_ANR_W)
  3802. {
  3803. BOOL bUnicode = (PROP_TYPE(lpres->res.resProperty.ulPropTag) == PT_UNICODE);
  3804. // First check the display name
  3805. lpprop = LpSPropValueFindColumn(lprow, bUnicode ? PR_DISPLAY_NAME_W : PR_DISPLAY_NAME_A);
  3806. if(lpprop)
  3807. {
  3808. LPTSTR lpT = bUnicode ? lpprop->Value.lpszW : ConvertAtoW(lpprop->Value.lpszA);
  3809. LPTSTR lpS = bUnicode ? lpres->res.resProperty.lpProp->Value.lpszW :
  3810. ConvertAtoW(lpres->res.resProperty.lpProp->Value.lpszA);
  3811. // Do a fuzzy search on this property's string value
  3812. *pfSatisfies = SubstringSearch( lpT, lpS );
  3813. if(!bUnicode)
  3814. {
  3815. LocalFreeAndNull(&lpT);
  3816. LocalFreeAndNull(&lpS);
  3817. }
  3818. }
  3819. if(!*pfSatisfies)
  3820. {
  3821. //Display name not found or not matched .. check e-mail address
  3822. lpprop = LpSPropValueFindColumn(lprow, bUnicode ? PR_EMAIL_ADDRESS_W : PR_EMAIL_ADDRESS_A);
  3823. if(lpprop)
  3824. {
  3825. LPTSTR lpT = bUnicode ? lpprop->Value.lpszW : ConvertAtoW(lpprop->Value.lpszA);
  3826. LPTSTR lpS = bUnicode ? lpres->res.resProperty.lpProp->Value.lpszW :
  3827. ConvertAtoW(lpres->res.resProperty.lpProp->Value.lpszA);
  3828. // Do a fuzzy search on this property's string value
  3829. *pfSatisfies = SubstringSearch( lpT, lpS );
  3830. if(!bUnicode)
  3831. {
  3832. LocalFreeAndNull(&lpT);
  3833. LocalFreeAndNull(&lpS);
  3834. }
  3835. }
  3836. }
  3837. }
  3838. else
  3839. {
  3840. lpprop = LpSPropValueFindColumn(lprow, lpres->res.resProperty.ulPropTag);
  3841. if(lpprop)
  3842. {
  3843. *pfSatisfies = FPropCompareProp(
  3844. lpprop,
  3845. lpres->res.resProperty.relop,
  3846. lpres->res.resProperty.lpProp);
  3847. }
  3848. }
  3849. goto ret;
  3850. }
  3851. case RES_COMPAREPROPS:
  3852. {
  3853. LPSPropValue lpprop1;
  3854. LPSPropValue lpprop2;
  3855. lpprop1 = LpSPropValueFindColumn(lprow, lpres->res.resCompareProps.ulPropTag1);
  3856. lpprop2 = LpSPropValueFindColumn(lprow, lpres->res.resCompareProps.ulPropTag2);
  3857. *pfSatisfies = (lpprop1 && lpprop2) ?
  3858. FPropCompareProp(
  3859. lpprop1,
  3860. lpres->res.resCompareProps.relop,
  3861. lpprop2) :
  3862. FALSE;
  3863. goto ret;
  3864. }
  3865. case RES_BITMASK:
  3866. {
  3867. LPSPropValue lpprop;
  3868. lpprop = LpSPropValueFindColumn(lprow, lpres->res.resBitMask.ulPropTag);
  3869. *pfSatisfies = lpprop ?
  3870. ((ULONG) !!(lpprop->Value.l &
  3871. lpres->res.resBitMask.ulMask) ==
  3872. lpres->res.resBitMask.relBMR) :
  3873. FALSE;
  3874. goto ret;
  3875. }
  3876. case RES_SIZE:
  3877. {
  3878. LPSPropValue lpprop;
  3879. LONG ldcb;
  3880. *pfSatisfies = FALSE;
  3881. if ( (lpprop = LpSPropValueFindColumn(
  3882. lprow,
  3883. lpres->res.resSize.ulPropTag)) != NULL )
  3884. {
  3885. ldcb = (LONG) lpres->res.resSize.cb - (LONG) UlPropSize(lpprop);
  3886. switch (lpres->res.resSize.relop)
  3887. {
  3888. case RELOP_LT:
  3889. *pfSatisfies = (0 < ldcb);
  3890. break;
  3891. case RELOP_LE:
  3892. *pfSatisfies = (0 <= ldcb);
  3893. break;
  3894. case RELOP_GT:
  3895. *pfSatisfies = (0 > ldcb);
  3896. break;
  3897. case RELOP_GE:
  3898. *pfSatisfies = (0 >= ldcb);
  3899. break;
  3900. case RELOP_EQ:
  3901. *pfSatisfies = (0 == ldcb);
  3902. break;
  3903. case RELOP_NE:
  3904. *pfSatisfies = (0 != ldcb);
  3905. break;
  3906. }
  3907. }
  3908. goto ret;
  3909. }
  3910. case RES_EXIST:
  3911. {
  3912. *pfSatisfies = !!LpSPropValueFindColumn(
  3913. lprow,
  3914. lpres->res.resExist.ulPropTag);
  3915. goto ret;
  3916. }
  3917. case RES_SUBRESTRICTION:
  3918. {
  3919. sc = MAPI_E_TOO_COMPLEX;
  3920. goto ret;
  3921. }
  3922. default:
  3923. {
  3924. TrapSz( TEXT("ITABLE:ScSatisfiesRestriction - Bad restriction type"));
  3925. }
  3926. }
  3927. ret:
  3928. return sc;
  3929. }
  3930. /*============================================================================
  3931. - LpSPropValueFindColumn()
  3932. -
  3933. * Utility function to find a column in a row given its proptag.
  3934. * NOTE! This function compares the entire prop tag. PROP_TYPEs MUST
  3935. * match!
  3936. * Returns:
  3937. * a pointer to the column found or, if the row doesn't contain the
  3938. * specified column, returns NULL.
  3939. */
  3940. LPSPropValue __fastcall
  3941. LpSPropValueFindColumn(
  3942. LPSRow lprow,
  3943. ULONG ulPropTagColumn )
  3944. {
  3945. ULONG i = 0;
  3946. for (i=0;i<lprow->cValues;i++)
  3947. {
  3948. if((lprow->lpProps)[i].ulPropTag == ulPropTagColumn)
  3949. return (&(lprow->lpProps[i]));
  3950. }
  3951. /* if(ulPropTagColumn == PR_ANR)
  3952. {
  3953. // This is a special case table restriction
  3954. if( lpprop->ulPropTag == PR_DISPLAY_NAME ||
  3955. lpprop->ulPropTag == PR_EMAIL_ADDRESS )
  3956. return lpprop;
  3957. }
  3958. else
  3959. */
  3960. return NULL;
  3961. }
  3962. /*============================================================================
  3963. - ScBufAllocateMore()
  3964. -
  3965. * MAPIAllocateMore-compatible function to use with proputil's
  3966. * PropCopyMore when copying into an already allocated buffer.
  3967. * It avoids having PropCopyMore calling MAPIAllocateMore (which
  3968. * continually allocates memory from the system) resulting
  3969. * in faster copying at the expense of running through the properties
  3970. * to copy once first to determine the amount of additional memory
  3971. * needed to copy them (see UlcbPropToCopy() below).
  3972. */
  3973. STDMETHODIMP_(SCODE)
  3974. ScBufAllocateMore(
  3975. ULONG ulcb,
  3976. LPCMB lpcmb,
  3977. LPVOID FAR * lplpv )
  3978. {
  3979. ulcb = LcbAlignLcb(ulcb);
  3980. if ( ulcb > lpcmb->ulcb )
  3981. {
  3982. TrapSz( TEXT("ScBufAllocateMore() - Buffer wasn't big enough for allocations\n") );
  3983. return MAPI_E_NOT_ENOUGH_MEMORY;
  3984. }
  3985. *((UNALIGNED LPVOID FAR*) lplpv) = lpcmb->lpv;
  3986. (LPBYTE) lpcmb->lpv += ulcb;
  3987. lpcmb->ulcb -= ulcb;
  3988. return S_OK;
  3989. }
  3990. /*============================================================================
  3991. - UlcbPropToCopy()
  3992. -
  3993. * Not to be confused with UlPropSize in proputil!
  3994. *
  3995. * UlcbPropToCopy() returns the size, in bytes, needed to store the value
  3996. * portion of a prop value not including the size of the SPropValue
  3997. * structure itself plus any necessary alignment padding.
  3998. * E.g. A PT_I2 property would always have a size equal to
  3999. * sizeof(SPropValue); a PT_BINARY property would have a size
  4000. * equal to sizeof(SPropValue) + ALIGN(Value.bin.cb).
  4001. */
  4002. ULONG
  4003. UlcbPropToCopy( LPSPropValue lpprop )
  4004. {
  4005. ULONG ulcb = 0;
  4006. LPVOID lpv;
  4007. UNALIGNED LPWSTR FAR * lplpwstr = NULL;
  4008. switch ( PROP_TYPE(lpprop->ulPropTag) )
  4009. {
  4010. case PT_I2:
  4011. case PT_LONG:
  4012. case PT_R4:
  4013. case PT_APPTIME:
  4014. case PT_DOUBLE:
  4015. case PT_BOOLEAN:
  4016. case PT_CURRENCY:
  4017. case PT_SYSTIME:
  4018. case PT_I8:
  4019. case PT_ERROR:
  4020. return 0;
  4021. case PT_CLSID:
  4022. return LcbAlignLcb(sizeof(CLSID));
  4023. case PT_BINARY:
  4024. return LcbAlignLcb(lpprop->Value.bin.cb);
  4025. case PT_STRING8:
  4026. return LcbAlignLcb((lstrlenA(lpprop->Value.lpszA)+1) *
  4027. sizeof(CHAR));
  4028. #ifndef WIN16
  4029. case PT_UNICODE:
  4030. // ((UNALIGNED LPWSTR *) lpv1) = &(lpprop->Value.lpszW);
  4031. ulcb = (ULONG) lstrlenW((LPWSTR) lpprop->Value.lpszW);
  4032. return LcbAlignLcb((ulcb + 1) * sizeof(WCHAR));
  4033. #endif // !WIN16
  4034. case PT_MV_I2:
  4035. ulcb = sizeof(short int);
  4036. break;
  4037. case PT_MV_LONG:
  4038. ulcb = sizeof(LONG);
  4039. break;
  4040. case PT_MV_R4:
  4041. ulcb = sizeof(float);
  4042. break;
  4043. case PT_MV_APPTIME:
  4044. case PT_MV_DOUBLE:
  4045. ulcb = sizeof(double);
  4046. break;
  4047. case PT_MV_CURRENCY:
  4048. ulcb = sizeof(CURRENCY);
  4049. break;
  4050. case PT_MV_SYSTIME:
  4051. ulcb = sizeof(FILETIME);
  4052. break;
  4053. case PT_MV_I8:
  4054. ulcb = sizeof(LARGE_INTEGER);
  4055. break;
  4056. case PT_MV_BINARY:
  4057. {
  4058. ulcb = lpprop->Value.MVbin.cValues * sizeof(SBinary);
  4059. lpv = lpprop->Value.MVbin.lpbin + lpprop->Value.MVbin.cValues;
  4060. while ( ((SBinary FAR *)lpv)-- > lpprop->Value.MVbin.lpbin )
  4061. ulcb += LcbAlignLcb(((SBinary FAR *)lpv)->cb);
  4062. return LcbAlignLcb(ulcb);
  4063. }
  4064. case PT_MV_STRING8:
  4065. {
  4066. ulcb = sizeof(LPSTR) * lpprop->Value.MVszA.cValues;
  4067. lpv = lpprop->Value.MVszA.lppszA + lpprop->Value.MVszA.cValues;
  4068. while ( ((LPSTR FAR *)lpv)-- > lpprop->Value.MVszA.lppszA )
  4069. ulcb += (lstrlenA(*(LPSTR FAR *)lpv)+1) * sizeof(CHAR);
  4070. return LcbAlignLcb(ulcb);
  4071. }
  4072. #ifndef WIN16
  4073. case PT_MV_UNICODE:
  4074. {
  4075. ulcb = sizeof(LPWSTR) * lpprop->Value.MVszW.cValues;
  4076. // lpv1 = lpprop->Value.MVszW.lppszW;
  4077. lplpwstr = lpprop->Value.MVszW.lppszW;
  4078. lplpwstr += lpprop->Value.MVszW.cValues;
  4079. while (lplpwstr-- > ((UNALIGNED LPWSTR * ) lpprop->Value.MVszW.lppszW) )
  4080. ulcb += (lstrlenW(*lplpwstr)+1) * sizeof(WCHAR);
  4081. return LcbAlignLcb(ulcb);
  4082. }
  4083. #endif // !WIN16
  4084. }
  4085. // For multi-valued arrays of constant-size objects...
  4086. return lpprop->Value.MVi.cValues * LcbAlignLcb(ulcb);
  4087. }
  4088. /*============================================================================
  4089. - FRowContainsProp()
  4090. -
  4091. * Determines whether or not the given row contains and matches the given
  4092. * property value array.
  4093. *
  4094. * Returns TRUE if the row contains and matches all the propery values
  4095. *
  4096. * Parameters:
  4097. * lprow in Row to examine
  4098. * cValues in number of property values
  4099. * lpsv in property value array to be compared
  4100. */
  4101. BOOL
  4102. FRowContainsProp(
  4103. LPSRow lprow,
  4104. ULONG cValues,
  4105. LPSPropValue lpsv)
  4106. {
  4107. ULONG uliProp;
  4108. LPSPropValue lpsvT;
  4109. Assert(lprow);
  4110. Assert(lpsv);
  4111. for (uliProp=0; uliProp < cValues; uliProp++)
  4112. {
  4113. // find the column with the same property tag
  4114. lpsvT=LpSPropValueFindColumn(lprow,lpsv[uliProp].ulPropTag);
  4115. if (lpsvT==NULL)
  4116. return FALSE;
  4117. // do the properties match?
  4118. if (LPropCompareProp(lpsvT,&lpsv[uliProp])!=0)
  4119. return FALSE;
  4120. }
  4121. return TRUE;
  4122. }
  4123. /*============================================================================
  4124. - FBookmarkStale()
  4125. -
  4126. * If a bookmark is Moving this function determines its uliRow and
  4127. * returns FALSE.
  4128. *
  4129. * If a bookmark has a uliRow that is too big then it is adjusted to
  4130. * point to the end of the table and marked as Changed. FALSE is returned.
  4131. *
  4132. * If a bookmark is marked as Stale or can't be adjusted then it
  4133. * is remarked as Stale and TRUE is returned
  4134. *
  4135. * Parameters:
  4136. * lpvue in View to check bookmark against
  4137. * bk in BOOKMARK to check
  4138. */
  4139. BOOL
  4140. FBookMarkStale( LPVUE lpvue,
  4141. BOOKMARK bk)
  4142. {
  4143. PBK pbk;
  4144. LPSRow * plprowBk;
  4145. // bk too big should already have been caught!
  4146. Assert( bk < cBookmarksMax);
  4147. pbk = lpvue->rgbk + bk;
  4148. if (pbk->dwfBKS & dwfBKSStale)
  4149. {
  4150. return TRUE;
  4151. }
  4152. if ( !(pbk->dwfBKS & dwfBKSMoving)
  4153. && (pbk->uliRow > lpvue->bkEnd.uliRow))
  4154. {
  4155. pbk->uliRow = lpvue->bkEnd.uliRow;
  4156. }
  4157. else if (plprowBk = PlprowByLprow( lpvue->bkEnd.uliRow
  4158. , lpvue->parglprows
  4159. , pbk->lprow))
  4160. {
  4161. pbk->uliRow = (ULONG) (plprowBk - lpvue->parglprows);
  4162. pbk->dwfBKS &= ~dwfBKSMoving;
  4163. }
  4164. else if (pbk->dwfBKS & dwfBKSMoving)
  4165. {
  4166. TrapSz( TEXT("Moving bookmark lost its row.\n"));
  4167. pbk->dwfBKS = dwfBKSValid | dwfBKSStale;
  4168. return TRUE;
  4169. }
  4170. return FALSE;
  4171. }
  4172. #ifdef WIN16 // Imported INLINE function.
  4173. /*============================================================================
  4174. - FFindColumn()
  4175. -
  4176. * Checks a prop tag array to see if a given prop tag exists.
  4177. *
  4178. * NOTE! The prop tag must match completely (even type).
  4179. *
  4180. *
  4181. * Parameters:
  4182. * lpptaCols in Prop tag array to check
  4183. * ulPropTag in Prop tag to check for.
  4184. *
  4185. * Returns:
  4186. * TRUE if ulPropTag is in lpptaCols
  4187. * FALSE if ulPropTag is not in lpptaCols
  4188. */
  4189. BOOL
  4190. FFindColumn( LPSPropTagArray lpptaCols,
  4191. ULONG ulPropTag )
  4192. {
  4193. ULONG * pulPropTag;
  4194. pulPropTag = lpptaCols->aulPropTag + lpptaCols->cValues;
  4195. while ( --pulPropTag >= lpptaCols->aulPropTag )
  4196. if ( *pulPropTag == ulPropTag )
  4197. return TRUE;
  4198. return FALSE;
  4199. }
  4200. /*============================================================================
  4201. - ScFindRow()
  4202. -
  4203. * Finds the first row in the table data whose index column property
  4204. * value is equal to that of the specified property and returns the
  4205. * location of that row in the table data, or, if no such row exists,
  4206. * the end of the table data.
  4207. *
  4208. * Parameters:
  4209. * lptad in TAD in which to find row
  4210. * lpprop in Index property to match
  4211. * puliRow out Pointer to location of found row
  4212. *
  4213. * Error returns:
  4214. * MAPI_E_INVALID_PARAMETER If proptag of property isn't the TAD's
  4215. * index column's proptag.
  4216. * MAPI_E_NOT_FOUND If no matching row is found (*pplprow
  4217. * is set to lptad->parglprows +
  4218. * lptad->cRows in this case).
  4219. */
  4220. SCODE
  4221. ScFindRow(
  4222. LPTAD lptad,
  4223. LPSPropValue lpprop,
  4224. LPSRow * * pplprow)
  4225. {
  4226. SCODE sc = S_OK;
  4227. SRow row;
  4228. SizedSSortOrderSet(1, sosIndex) = { 1, 0, 0 };
  4229. row.ulAdrEntryPad = 0;
  4230. row.cValues = 1;
  4231. row.lpProps = lpprop;
  4232. if (lpprop->ulPropTag != lptad->ulPropTagIndexCol)
  4233. {
  4234. sc = MAPI_E_INVALID_PARAMETER;
  4235. goto ret;
  4236. }
  4237. Assert(!IsBadWritePtr(pplprow, sizeof(*pplprow)));
  4238. // Build a sort order set for the Index Column
  4239. sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol;
  4240. sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND;
  4241. *pplprow = PlprowCollateRow(lptad->ulcRowsIndex,
  4242. lptad->parglprowIndex,
  4243. (LPSSortOrderSet) &sosIndex,
  4244. FALSE,
  4245. &row);
  4246. // Find the row in the Index Sorted Row Set
  4247. if ( !lptad->ulcRowsIndex
  4248. || (*pplprow >= (lptad->parglprowIndex + lptad->ulcRowsIndex))
  4249. || LPropCompareProp( lpprop, (**pplprow)->lpProps))
  4250. {
  4251. sc = MAPI_E_NOT_FOUND;
  4252. }
  4253. ret:
  4254. return sc;
  4255. }
  4256. #endif // WIN16
  4257. /*
  4258. -
  4259. - FixupCols
  4260. *
  4261. */
  4262. void FixupColsWA(LPSPropTagArray lpptaCols, BOOL bUnicodeTable)
  4263. {
  4264. if(!bUnicodeTable) //<note> assumes UNICODE is defined
  4265. {
  4266. // We need to mark the table columns as not having UNICODE props
  4267. ULONG i = 0;
  4268. for(i = 0;i<lpptaCols->cValues;i++)
  4269. {
  4270. switch(PROP_TYPE(lpptaCols->aulPropTag[i]))
  4271. {
  4272. case PT_UNICODE:
  4273. lpptaCols->aulPropTag[i] = CHANGE_PROP_TYPE(lpptaCols->aulPropTag[i], PT_STRING8);
  4274. break;
  4275. case PT_MV_UNICODE:
  4276. lpptaCols->aulPropTag[i] = CHANGE_PROP_TYPE(lpptaCols->aulPropTag[i], PT_MV_STRING8);
  4277. break;
  4278. }
  4279. }
  4280. }
  4281. }