/*============================================================================ * * ITABLE.C * * MAPI 1.0 In-memory MAPI Table DLL (MAPIU.DLL) * * Copyright (C) 1993 and 1994 Microsoft Corporation * * * Hungarian shorthand: * To avoid excessively long identifier names, the following * shorthand expressions are used: * * LPSPropTagArray lppta * LPSRestriction lpres * LPSPropValue lpprop * LPSRow lprow * LPSRowSet lprows * LPSSortOrder lpso * LPSSortOrderSet lpsos * * Known bugs: * - Restriction evaulation/copying is recursive * - Uses hinst derived from static module name (Raid 1263) */ #include "_apipch.h" void FixupColsWA(LPSPropTagArray lpptaCols, BOOL bUnicodeTable); /*============================================================================ * TAD (table data class) * * Implementes in-memory table data object. */ TAD_Vtbl vtblTAD = { VTABLE_FILL (TAD_QueryInterface_METHOD FAR *) UNKOBJ_QueryInterface, (TAD_AddRef_METHOD FAR *) UNKOBJ_AddRef, TAD_Release, TAD_HrGetView, TAD_HrModifyRow, TAD_HrDeleteRow, TAD_HrQueryRow, TAD_HrEnumRow, TAD_HrNotify, //$PERFORMANCE TAD_HrInsertRow, TAD_HrModifyRows, TAD_HrDeleteRows }; LPIID rglpiidTAD[2] = { (LPIID)&IID_IMAPITableData, (LPIID)&IID_IUnknown }; /*============================================================================ * VUE (table view class) * * Implementes in-memory IMAPITable class on top of TADs */ VUE_Vtbl vtblVUE = { VTABLE_FILL (VUE_QueryInterface_METHOD FAR *) UNKOBJ_QueryInterface, (VUE_AddRef_METHOD FAR *) UNKOBJ_AddRef, VUE_Release, (VUE_GetLastError_METHOD FAR *) UNKOBJ_GetLastError, VUE_Advise, VUE_Unadvise, VUE_GetStatus, VUE_SetColumns, VUE_QueryColumns, VUE_GetRowCount, VUE_SeekRow, VUE_SeekRowApprox, VUE_QueryPosition, VUE_FindRow, VUE_Restrict, VUE_CreateBookmark, VUE_FreeBookmark, VUE_SortTable, VUE_QuerySortOrder, VUE_QueryRows, VUE_Abort, VUE_ExpandRow, VUE_CollapseRow, VUE_WaitForCompletion, VUE_GetCollapseState, VUE_SetCollapseState }; LPIID rglpiidVUE[2] = { (LPIID)&IID_IMAPITable, (LPIID)&IID_IUnknown }; /*============================================================================ - CreateTable() - * * ulFlags - 0 or MAPI_UNICODE */ // // BUGBUG // [PaulHi] 4/5/99 @bug // A zero is passed in to CreateTableData() ulFlags parameter. This means that the // requested table is ALWAYS ANSI and CANNOT be UNICODE, regardless of the properties // passed in through the LPSPropTagArray. The CreateTableData() function will forcibly // set the property types to PT_STRING8 or PT_UNICODE depending on the ulFlags and since // the ulFlags is hard coded to zero this means always STRING8 string properties. // STDAPI_(SCODE) CreateTable(LPCIID lpiid, ALLOCATEBUFFER FAR * lpfAllocateBuffer, ALLOCATEMORE FAR * lpfAllocateMore, FREEBUFFER FAR * lpfFreeBuffer, LPVOID lpvReserved, ULONG ulTableType, ULONG ulPropTagIndexCol, LPSPropTagArray lpptaCols, LPTABLEDATA FAR * lplptad) { return(CreateTableData(lpiid, lpfAllocateBuffer, lpfAllocateMore, lpfFreeBuffer, lpvReserved, ulTableType, ulPropTagIndexCol, lpptaCols, NULL, // lpvDataSource 0, // cbDataSource NULL, 0, // ulFlags, includes MAPI_UNICODE, which is hard coded to ANSI!!! lplptad)); } /* - - CreateTableData * * ulFlags - 0 | MAPI_UNICODE | WAB_PROFILE_CONTENTS | WAB_ENABLE_PROFILES * */ STDAPI_(SCODE) CreateTableData(LPCIID lpiid, ALLOCATEBUFFER FAR * lpfAllocateBuffer, ALLOCATEMORE FAR * lpfAllocateMore, FREEBUFFER FAR * lpfFreeBuffer, LPVOID lpvReserved, ULONG ulTableType, ULONG ulPropTagIndexCol, LPSPropTagArray lpptaCols, LPVOID lpvDataSource, ULONG cbDataSource, LPSBinary pbinContEID, ULONG ulFlags, LPTABLEDATA FAR * lplptad) { LPTAD lptad = NULL; SCODE sc; ULONG ulIndexType = PROP_TYPE(ulPropTagIndexCol); #if !defined(NO_VALIDATION) if ( lpiid && IsBadReadPtr(lpiid,sizeof(IID)) || IsBadCodePtr((FARPROC)lpfAllocateBuffer) || IsBadCodePtr((FARPROC)lpfAllocateMore) || IsBadCodePtr((FARPROC)lpfFreeBuffer) || (ulTableType != TBLTYPE_SNAPSHOT && ulTableType != TBLTYPE_KEYSET && ulTableType != TBLTYPE_DYNAMIC) || !PROP_ID(ulPropTagIndexCol) || (ulIndexType == PT_UNSPECIFIED) || (ulIndexType == PT_NULL) || (ulIndexType == PT_ERROR) || (ulIndexType & MV_FLAG) || FBadColumnSet(lpptaCols) || IsBadWritePtr(lplptad,sizeof(LPTABLEDATA)) ) { DebugTrace(TEXT("CreateTable() - Bad parameter(s) passed\n") ); return MAPI_E_INVALID_PARAMETER; } #endif // Verify caller wants an IMAPITableData interface if ( lpiid && memcmp(lpiid, &IID_IMAPITableData, sizeof(IID)) ) { DebugTrace(TEXT("CreateTable() - Unknown interface ID passed\n") ); return MAPI_E_INTERFACE_NOT_SUPPORTED; } // Instantiate a new table data object if ( FAILED(sc = lpfAllocateBuffer(sizeof(TAD), (LPVOID FAR *) &lptad)) ) { DebugTrace(TEXT("CreateTable() - Error instantiating new TAD (SCODE = 0x%08lX)\n"), sc ); goto err; } MAPISetBufferName(lptad, TEXT("ITable TAD object")); ZeroMemory(lptad, sizeof(TAD)); if (lpvDataSource) { if (cbDataSource) { LPTSTR lpNew; if (! (lpNew = LocalAlloc(LPTR, cbDataSource))) { DebugTrace(TEXT("CreateTable:LocalAlloc(%u) -> %u\n"), cbDataSource, GetLastError()); sc = MAPI_E_NOT_ENOUGH_MEMORY; goto err; } CopyMemory(lpNew, lpvDataSource, cbDataSource); lptad->lpvDataSource = lpNew; } else { lptad->lpvDataSource = lpvDataSource; // no size, just a pointer. DON'T Free! } lptad->cbDataSource = cbDataSource; } else { lptad->cbDataSource = 0; lptad->lpvDataSource = NULL; } lptad->pbinContEID = pbinContEID; //if(!pbinContEID || (!pbinContEID->cb && !pbinContEID->lpb)) // This is the PAB container // The caller will send in container EIDs which will be: // if PAB = User Folder, cont EID won't be NULL - return folder contents only // if PAB = Virtual Folder, cont EID will have 0 and NULL in it - return all WAB contents // if WAB_PROFILE_CONTENTS is specified, just return all the contents of all the folders in the profile if(ulFlags & WAB_PROFILE_CONTENTS) lptad->bAllProfileContents = TRUE; // this forces folder contents only if(ulFlags & MAPI_UNICODE) lptad->bMAPIUnicodeTable = TRUE; if(pbinContEID && pbinContEID->cb && pbinContEID->lpb) // This is not the Virtual PAB container lptad->bContainerContentsOnly = (ulFlags & WAB_ENABLE_PROFILES); else lptad->bContainerContentsOnly = FALSE; lptad->inst.lpfAllocateBuffer = lpfAllocateBuffer; lptad->inst.lpfAllocateMore = lpfAllocateMore; lptad->inst.lpfFreeBuffer = lpfFreeBuffer; #ifdef MAC lptad->inst.hinst = hinstMapiX;//GetCurrentProcess(); #else lptad->inst.hinst = hinstMapiX;//HinstMapi(); #ifdef DEBUG if (lptad->inst.hinst == NULL) TraceSz1( TEXT("ITABLE: GetModuleHandle failed with error %08lX"), GetLastError()); #endif /* DEBUG */ #endif /* MAC */ if (FAILED(sc = UNKOBJ_Init( (LPUNKOBJ) lptad , (UNKOBJ_Vtbl FAR *) &vtblTAD , sizeof(vtblTAD) , rglpiidTAD , sizeof(rglpiidTAD)/sizeof(REFIID) , &lptad->inst))) { DebugTrace(TEXT("CreateTable() - Error initializing object (SCODE = 0x%08lX)\n"), sc ); goto err; } lptad->ulTableType = ulTableType; lptad->ulPropTagIndexCol = ulPropTagIndexCol; if ( FAILED(sc = ScCOAllocate(lptad, CbNewSPropTagArray(lpptaCols->cValues), &lptad->lpptaCols)) ) { DebugTrace(TEXT("CreateTable() - Error duping initial column set (SCODE = 0x%08lX)\n"), sc ); goto err; } lptad->ulcColsMac = lpptaCols->cValues; CopyMemory(lptad->lpptaCols, lpptaCols, (size_t) (CbNewSPropTagArray(lpptaCols->cValues))); // [PaulHi] 4/5/99 @comment Shouldn't // clients of the WAB request ANSI/UNICODE based on property tags in the // column array, rather than doing mass conversions? // Seems like the fix is to create two versions of column property arrays, // an ANSI and Unicode version. FixupColsWA(lptad->lpptaCols, (ulFlags & MAPI_UNICODE)); // And return it *lplptad = (LPTABLEDATA) lptad; ret: DebugTraceSc(CreateTable, sc); return sc; err: UlRelease(lptad); goto ret; } /*============================================================================ - TAD::Release() - */ STDMETHODIMP_(ULONG) TAD_Release( LPTAD lptad ) { ULONG ulcRef; LPSRow * plprow; #if !defined(NO_VALIDATION) if ( BAD_STANDARD_OBJ(lptad,TAD_,Release,lpVtbl) ) { TraceSz( TEXT("TAD::Release() - Invalid parameter passed as TAD object")); return !0; } #endif LockObj(lptad); if (ulcRef = lptad->ulcRef) { ulcRef = --lptad->ulcRef; } if ( ulcRef == 0 && !lptad->lpvueList ) { UnlockObj(lptad); //$ Do we need this? COFree(lptad, lptad->lpptaCols); if (lptad->cbDataSource && lptad->lpvDataSource) { LocalFreeAndNull(&lptad->lpvDataSource); } plprow = lptad->parglprowAdd + lptad->ulcRowsAdd; while ( plprow-- > lptad->parglprowAdd ) ScFreeBuffer(lptad, *plprow); COFree(lptad, lptad->parglprowAdd); COFree(lptad, lptad->parglprowIndex); UNKOBJ_Deinit((LPUNKOBJ) lptad); ScFreeBuffer(lptad, lptad); } else { #if DEBUG if ( ulcRef == 0 && lptad->lpvueList ) { TraceSz( TEXT("TAD::Release() - TAD object still has open views")); } #endif // DEBUG UnlockObj(lptad); } return ulcRef; } /*============================================================================ - TAD::HrGetView() - * A NULL lpsos means that rows will be in the order that they were added * to the TAD. */ STDMETHODIMP TAD_HrGetView( LPTAD lptad, LPSSortOrderSet lpsos, CALLERRELEASE FAR * lpfReleaseCallback, ULONG ulReleaseData, LPMAPITABLE FAR * lplpmt ) { SCODE sc; LPVUE lpvue = NULL; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lptad,TAD_,HrGetView,lpVtbl); if ( (lpsos && FBadSortOrderSet(lpsos)) || (lpfReleaseCallback && IsBadCodePtr((FARPROC) lpfReleaseCallback)) || IsBadWritePtr(lplpmt, sizeof(LPMAPITABLE)) ) { DebugTrace(TEXT("TAD::HrGetView() - Invalid parameter(s) passed\n") ); return ResultFromScode(MAPI_E_INVALID_PARAMETER); } #endif // Can't support categories if (lpsos && lpsos->cCategories) { DebugTrace(TEXT("TAD::GetView() - No support for categories\n") ); return ResultFromScode(MAPI_E_TOO_COMPLEX); } LockObj(lptad); // Instantiate a new table view if ( FAILED(sc = lptad->inst.lpfAllocateBuffer(sizeof(VUE), (LPVOID FAR *) &lpvue)) ) { DebugTrace(TEXT("ScCreateView() - Error instantiating VUE on TAD (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(lpvue, TEXT("ITable VUE object")); ZeroMemory(lpvue, sizeof(VUE)); if (FAILED(sc = UNKOBJ_Init( (LPUNKOBJ) lpvue , (UNKOBJ_Vtbl FAR *) &vtblVUE , sizeof(vtblVUE) , rglpiidVUE , sizeof(rglpiidVUE)/sizeof(REFIID) , lptad->pinst))) { DebugTrace(TEXT("ScCreateView() - Error initializing VUE object (SCODE = 0x%08lX)\n"), sc ); // don't try to release the vue since it wasn't initialized yet lptad->inst.lpfFreeBuffer(lpvue); goto ret; } // Link the view to the TAD and AddRef the TAD. lpvue->lpvueNext = lptad->lpvueList; lptad->lpvueList = lpvue; lpvue->lptadParent = lptad; UlAddRef(lptad); // Identifier for this table lpvue->cbDataSource = lptad->cbDataSource; lpvue->lpvDataSource = lptad->lpvDataSource; // Initialize the predefined bookmarks lpvue->bkBeginning.dwfBKS = dwfBKSValid; lpvue->bkCurrent.dwfBKS = dwfBKSValid; lpvue->bkEnd.dwfBKS = dwfBKSValid; #ifdef NOTIFICATIONS // Burn up a MUID for the notification key for this view if ( FAILED(sc = ScGenerateMuid(&lpvue->mapiuidNotif)) ) { DebugTrace(TEXT("TAD::HrGetView() - Error generating MUID for notification key (SCODE = 0x%08lX)\n"), sc ); goto err; } #endif // Make a copy of the initial sort order for the VUE if ( lpsos && FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lptad , CbSSortOrderSet(lpsos) , (LPBYTE) lpsos , 0 , (LPBYTE FAR *) &(lpvue->lpsos))) ) { DebugTrace(TEXT("TAD::GetView() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc ); goto err; } MAPISetBufferName(lpvue->lpsos, TEXT("ITable: dup sort order set")); // Load the view's initial row set in sorted order if ( FAILED(sc = ScLoadRows(lpvue->lptadParent->ulcRowsAdd, lpvue->lptadParent->parglprowAdd, lpvue, NULL, lpvue->lpsos)) ) { DebugTrace(TEXT("TAD::HrGetView() - Error loading view's initial row set (SCODE = 0x%08lX)\n"), sc ); goto err; } lpvue->bMAPIUnicodeTable = lptad->bMAPIUnicodeTable; // Set the view's initial column set if ( FAILED(sc = GetScode(VUE_SetColumns(lpvue, lptad->lpptaCols, 0))) ) { DebugTrace(TEXT("TAD::HrGetView() - Error setting view's initial column set (SCODE = 0x%08lX)\n"), sc ); goto err; } lpvue->lpfReleaseCallback = lpfReleaseCallback; lpvue->ulReleaseData = ulReleaseData; *lplpmt = (LPMAPITABLE) lpvue; ret: UnlockObj(lptad); return ResultFromScode(sc); err: // This will unlink and release the parent TAD. UlRelease(lpvue); goto ret; } /*============================================================================ - TAD::HrModifyRow() - */ STDMETHODIMP TAD_HrModifyRow( LPTAD lptad, LPSRow lprow ) { SizedSRowSet( 1, rowsetIn); rowsetIn.cRows = 1; rowsetIn.aRow[0] = *lprow; return TAD_HrModifyRows(lptad, 0, (LPSRowSet) &rowsetIn); } /*============================================================================ - TAD::HrModifyRows() - */ STDMETHODIMP TAD_HrModifyRows( LPTAD lptad, ULONG ulFlags, LPSRowSet lprowsetIn ) { ULONG cRowsCopy = 0; LPSRow * parglprowSortedCopy = NULL; LPSRow * parglprowUnsortedCopy = NULL; ULONG cRowsOld = 0; LPSRow * parglprowOld = NULL; ULONG cNewTags = 0; SCODE sc; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lptad,TAD_,HrModifyRows,lpVtbl); if ( IsBadReadPtr( lprowsetIn, CbNewSRowSet(0)) || IsBadWritePtr( lprowsetIn, CbSRowSet(lprowsetIn))) { DebugTrace(TEXT("TAD::HrModifyRows() - Invalid parameter(s) passed\n") ); return ResultFromScode(MAPI_E_INVALID_PARAMETER); } // Validation of the actual rows input will be done at the same time that // we make our internal copy. #endif if (ulFlags) { DebugTrace(TEXT("TAD::HrModifyRows() - Unknown flags passed\n") ); // return ResultFromScode(MAPI_E_UNKNOWN_FLAGS); } LockObj(lptad); // Make a copy of the rows for our own use. // Add new columns to the TAD's column set. // // Move the index column to the front. // Filter out PT_ERROR and PT_NULL columns. // Validate the input rows. // Note - two rows with the same index property is invalid. if ( FAILED(sc = ScCopyTadRowSet( lptad , lprowsetIn , &cNewTags , &cRowsCopy , &parglprowUnsortedCopy , &parglprowSortedCopy)) ) { DebugTrace(TEXT("TAD::HrModifyRows() - Error duping row set to modify\n") ); goto ret; } // Replace/add the copied row to the table data. We pass in the unsorted // Set in order to maintain the FIFO behaviour on unsorted views // Note! This call MUST replace all (SUCCESS) or none (FAILURE)! if ( FAILED(sc = ScReplaceRows( lptad , cRowsCopy , parglprowUnsortedCopy , &cRowsOld , &parglprowOld)) ) { DebugTrace(TEXT("TAD::HrModifyRows() - Error adding rows (SCODE = 0x%08lX)\n"), sc ); goto err; } // Update the views with the modified rows. // NOTE! Failure to update a view CANNOT leave a view pointing to an // old row! UpdateViews( lptad , cRowsOld , parglprowOld , cRowsCopy , parglprowUnsortedCopy , parglprowSortedCopy); // Free the old rows. if (parglprowOld) { LPSRow * plprowTmp = parglprowOld; while (cRowsOld) { ScFreeBuffer( lptad, *(plprowTmp++)); cRowsOld--; } } ret: // Free the tables of row pointers ScFreeBuffer( lptad, parglprowSortedCopy); ScFreeBuffer( lptad, parglprowUnsortedCopy); ScFreeBuffer(lptad, parglprowOld); UnlockObj(lptad); return ResultFromScode(sc); err: // Reset the TAD columns lptad->lpptaCols->cValues -= cNewTags; // On error the all copied rows are freed... if (parglprowSortedCopy) { LPSRow * plprowTmp = parglprowSortedCopy; while (cRowsCopy) { ScFreeBuffer( lptad, *(plprowTmp++)); cRowsCopy--; } } goto ret; } /*============================================================================ - TAD::HrDeleteRows() - */ STDMETHODIMP TAD_HrDeleteRows( LPTAD lptad, ULONG ulFlags, LPSRowSet lprowsetToDelete, ULONG FAR * lpcRowsDeleted ) { SCODE sc = S_OK; LPSRow lprowDelete; LPSRow * plprowIn; LPSRow * plprowOut; ULONG cRowsDeleted = 0; LPSRow * parglprowOld = NULL; LPSRow * * pargplprowOld; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lptad,TAD_,HrDeleteRow,lpVtbl); if ( ((ulFlags & TAD_ALL_ROWS) && lprowsetToDelete) || ( !(ulFlags & TAD_ALL_ROWS) && ( IsBadReadPtr( lprowsetToDelete, CbNewSRowSet(0)) || IsBadWritePtr( lprowsetToDelete , CbSRowSet(lprowsetToDelete)))) || (lpcRowsDeleted && IsBadWritePtr( lpcRowsDeleted, sizeof(ULONG)))) { DebugTrace(TEXT("TAD::HrDeleteRows() - Invalid parameter(s) passed\n") ); return ResultFromScode(MAPI_E_INVALID_PARAMETER); } #endif if (ulFlags & ~TAD_ALL_ROWS) { DebugTrace(TEXT("TAD::HrModifyRows() - Unknown flags passed\n") ); // return ResultFromScode(MAPI_E_UNKNOWN_FLAGS); } LockObj(lptad); if (ulFlags & TAD_ALL_ROWS) { cRowsDeleted = lptad->ulcRowsAdd; // // If there are any rows to delete // if (cRowsDeleted) { // // And they delete cleanly // if (FAILED(sc = ScDeleteAllRows( lptad))) { DebugTrace(TEXT("TAD::HrDeleteRows() - ScDeleteAllRows returned error (SCODE = 0x%08lX)\n"), sc ); goto ret; } } if (lpcRowsDeleted) { *lpcRowsDeleted = cRowsDeleted; } goto ret; } if (!lprowsetToDelete->cRows) { goto ret; } // Not allowed to delete rows from non-dynamic tables with open views if ( lptad->ulTableType != TBLTYPE_DYNAMIC && lptad->lpvueList ) { DebugTrace(TEXT("TAD::HrDeleteRows() - Operation not supported on non-dynamic TAD with open views\n") ); sc = MAPI_E_CALL_FAILED; goto ret; } // Allocate the list of old rows now so we won't fail after we start // adding rows. if (FAILED(sc = ScAllocateBuffer( lptad , lprowsetToDelete->cRows * sizeof(LPSRow) , &parglprowOld))) { DebugTrace(TEXT("ScAddRows() - Error creating old row list (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(parglprowOld, TEXT("ITable old row list")); // First we will try to find each row in the index sorted row list. // We won't delete them on the first pass since we must verify that // there aren't dups in the row set and that each row has an Index // property before we actually delete any rows. // Keep a list of pointers to the index sorted slot (argplprow) so that // we don't have to search again when we finally delete the rows. pargplprowOld = (LPSRow * *) parglprowOld; for ( lprowDelete = lprowsetToDelete->aRow + lprowsetToDelete->cRows ; lprowDelete-- > lprowsetToDelete->aRow ; ) { LPSRow * plprow = NULL; LPSPropValue lppropIndex; if (FBadRow(lprowDelete)) { DebugTrace(TEXT("TAD::HrDeleteRows() - Invalid row(s) passed\n") ); sc = MAPI_E_INVALID_PARAMETER; goto ret; } if (!(lppropIndex = PpropFindProp( lprowDelete->lpProps , lprowDelete->cValues , lptad->ulPropTagIndexCol))) { sc = MAPI_E_INVALID_PARAMETER; DebugTrace(TEXT("TAD::HrDeleteRows() - Row has no Index property.\n") ); goto ret; } sc = ScFindRow(lptad, lppropIndex, &plprow); if (sc == MAPI_E_NOT_FOUND) { // Don't try to delete rows that aren't in the table continue; } else if (FAILED(sc)) { // Something bad happened in ScFindRow so fail the call DebugTrace(TEXT("TAD::HrDeleteRows() - Error from ScFindRow.\n") ); goto ret; } // The row is valid and in the TAD so put its plprow in the table // to delete *(pargplprowOld++) = plprow; } sc = S_OK; // Now that we have completely validated the input row set and have made // a list of plprow's to delete from the Index sorted set, we can // actually delete them from the unsorted set and the index sorted set. // turn the list of plprows into a list of lprows for UpdateViews // The call is not allowed to fail after this point! cRowsDeleted = (ULONG) (((LPSRow *) pargplprowOld) - parglprowOld); while (((LPSRow *) pargplprowOld--) > parglprowOld) { LPSRow * plprow; LPSRow lprow = **pargplprowOld; // Remove the row from the unsorted row set if (plprow = PlprowByLprow( lptad->ulcRowsAdd , lptad->parglprowAdd , lprow)) { --lptad->ulcRowsAdd; MoveMemory( plprow , plprow + 1 , (size_t) ( (BYTE *)(lptad->parglprowAdd + lptad->ulcRowsAdd) - (BYTE *)(plprow))); } // The row should be in the unsorted set. Assert(plprow); // Remove the row from the Index sorted row set by // setting it to NULL. We'll squish the NULLs out later since we // don't know now what order they are in. **pargplprowOld = NULL; // Turn the plprow into an lprow to use in UpdateViews (LPSRow) (*pargplprowOld) = lprow; } // Remove the NULL pointers that were left in the Index sorted set for ( plprowOut = plprowIn = lptad->parglprowIndex ; (plprowIn < lptad->parglprowIndex + lptad->ulcRowsIndex) ; plprowIn++) { if (*plprowIn) { *plprowOut = *plprowIn; plprowOut++; } } lptad->ulcRowsIndex = (ULONG) (plprowOut - lptad->parglprowIndex); // Update and notify any affected views // using the converted argplprowOld (arglprowOld) UpdateViews(lptad, cRowsDeleted, parglprowOld, 0, NULL, NULL); if (lpcRowsDeleted) { *lpcRowsDeleted = cRowsDeleted; } ret: // Free the old rows. if (parglprowOld) { LPSRow * plprowOld; for ( plprowOld = parglprowOld + cRowsDeleted ; plprowOld-- > parglprowOld ; ) { ScFreeBuffer( lptad, *plprowOld); } ScFreeBuffer(lptad, parglprowOld); } UnlockObj(lptad); return ResultFromScode(sc); } /*============================================================================ - TAD::HrDeleteRow() - */ STDMETHODIMP TAD_HrDeleteRow ( LPTAD lptad, LPSPropValue lpprop ) { HRESULT hResult; SizedSRowSet(1, rowsetToDelete); ULONG cRowsDeleted; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lptad,TAD_,HrDeleteRow,lpVtbl); // Validation of lpprop done by TAD_HrDeleteRows #endif rowsetToDelete.cRows = 1; rowsetToDelete.aRow[0].cValues = 1; rowsetToDelete.aRow[0].lpProps = lpprop; if (HR_FAILED(hResult = TAD_HrDeleteRows( lptad , 0 , (LPSRowSet) &rowsetToDelete , &cRowsDeleted))) { DebugTrace(TEXT("TAD::HrDeleteRow() - Failed to delete rows.\n") ); goto ret; } Assert((cRowsDeleted == 1) || !cRowsDeleted); if (!cRowsDeleted) { DebugTrace(TEXT("TAD::HrDeleteRow() - Couldn't find row to delete.\n") ); hResult = ResultFromScode(MAPI_E_NOT_FOUND); } ret: return hResult; } /*============================================================================ - TAD::HrQueryRow() - */ STDMETHODIMP TAD_HrQueryRow( LPTAD lptad, LPSPropValue lpprop, LPSRow FAR * lplprow, ULONG * puliRow) { LPSRow * plprow = NULL; SCODE sc; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lptad,TAD_,HrQueryRow,lpVtbl); if ( FBadProp(lpprop) || IsBadWritePtr(lplprow, sizeof(LPSRow)) || (puliRow && IsBadWritePtr(puliRow, sizeof(*puliRow))) ) { DebugTrace(TEXT("TAD::HrQueryRow() - Invalid parameter(s) passed\n") ); return ResultFromScode(MAPI_E_INVALID_PARAMETER); } #endif LockObj(lptad); // Find the row if (FAILED(sc = ScFindRow(lptad, lpprop, &plprow))) { goto ret; } Assert(plprow); // Copy the row to return. Don't try to add new tags to the column set. if ( FAILED(sc = ScCopyTadRow( lptad, *plprow, NULL, lplprow )) ) { DebugTrace(TEXT("TAD::HrQueryRow() - Error making copy of row (SCODE = 0x%08lX)\n"), sc ); goto ret; } if (puliRow) { // Find the row from the unsorted row set plprow = PlprowByLprow( lptad->ulcRowsAdd, lptad->parglprowAdd, *plprow); // If the row was in the Index sorted set then it should be in // the unsorted set. Assert(plprow); *puliRow = (ULONG) (plprow - lptad->parglprowAdd); } ret: UnlockObj(lptad); return ResultFromScode(sc); } /*============================================================================ - TAD::HrEnumRow() - */ STDMETHODIMP TAD_HrEnumRow( LPTAD lptad, ULONG uliRow, LPSRow FAR * lplprow ) { SCODE sc; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lptad,TAD_,HrEnumRow,lpVtbl); if ( IsBadWritePtr(lplprow, sizeof(LPSRow)) ) { DebugTrace(TEXT("TAD::HrEnumRow() - Invalid parameter(s) passed\n") ); return ResultFromScode(MAPI_E_INVALID_PARAMETER); } #endif LockObj(lptad); if ( uliRow < lptad->ulcRowsAdd ) { // Copy the row to return. if ( FAILED(sc = ScCopyTadRow( lptad , lptad->parglprowAdd[uliRow] , NULL // Don't try to add new columns. , lplprow )) ) { DebugTrace(TEXT("TAD::HrEnumRow() - Error making copy of row (SCODE = 0x%08lX)\n"), sc ); goto ret; } } else { // Return NULL if row index is out of range *lplprow = NULL; sc = S_OK; } ret: UnlockObj(lptad); return ResultFromScode(sc); } /*============================================================================ - TAD_HrNotify - * Parameters: * lptad in the table object * ulFlags in flags (unused) * cValues in number of property values * lpsv in property value array to be compared */ STDMETHODIMP TAD_HrNotify( LPTAD lptad, ULONG ulFlags, ULONG cValues, LPSPropValue lpspv) { #ifdef NOTIFICATION NOTIFICATION notif; VUENOTIFKEY vuenotifkey; ULONG uliRow; ULONG ulNotifFlags; ULONG uliProp; LPSRow lprow; SCODE sc; LPVUE lpvue; #endif // NOTIFICATIONS #if !defined(NO_VALIDATION) if ( BAD_STANDARD_OBJ(lptad,TAD_,HrNotify,lpVtbl) ) { DebugTrace(TEXT("TAD::HrNotify() - Invalid parameter passed as TAD object\n") ); return ResultFromScode(MAPI_E_INVALID_PARAMETER); } if (cValues > UINT_MAX/sizeof(SPropValue) || IsBadReadPtr(lpspv,((UINT)cValues*sizeof(SPropValue)))) { DebugTrace(TEXT("TAD::HrNotify() - Invalid parameter passed as prop value array\n") ); return ResultFromScode(MAPI_E_INVALID_PARAMETER); } #endif if (cValues==0) { DebugTrace(TEXT("TAD::HrNotify() - zero props in prop value array?? \n") ); return hrSuccess; } #ifdef NOTIFICATIONS ZeroMemory(¬if, sizeof(NOTIFICATION)); notif.ulEventType = fnevTableModified; notif.info.tab.ulTableEvent = TABLE_ROW_MODIFIED; LockObj(lptad); for ( lpvue = (LPVUE) lptad->lpvueList; lpvue != NULL; lpvue = (LPVUE) lpvue->lpvueNext ) { AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE)) && lpvue->lpVtbl == &vtblVUE, TEXT("Bad lpvue in TAD vue List.") ); for (uliRow=0; uliRow < lpvue->bkEnd.uliRow; uliRow++) { lprow = lpvue->parglprows[uliRow]; // does the row contain matching properties? if (!FRowContainsProp(lprow,cValues,lpspv)) continue; // it doesn't so go on to next row // copy the row for the client sc=ScCopyVueRow(lpvue,lpvue->lpptaCols,lprow,¬if.info.tab.row); if (FAILED(sc)) { DebugTrace(TEXT("TAD_HrNotify() - VUE_ScCopyRow return %s\n"), SzDecodeScode(sc)); continue; } notif.info.tab.propIndex=*lpspv; // Fill in index property of row previous to row // modified. If row modified was first // row, fill in 0 for proptag of index property of // previous row. if (uliRow == 0) { ZeroMemory(¬if.info.tab.propPrior, sizeof(SPropValue)); notif.info.tab.propPrior.ulPropTag = PR_NULL; } else { // point to previous row lprow = lpvue->parglprows[uliRow-1]; for (uliProp=0; uliProp < lprow->cValues; uliProp++) { if (lprow->lpProps[uliProp].ulPropTag==lpspv->ulPropTag) break; } // should have found the index property Assert(uliProp < lprow->cValues); notif.info.tab.propPrior = lprow->lpProps[uliProp]; } // Kick off notifications to all the notifications open on the view vuenotifkey.ulcb = sizeof(MAPIUID); vuenotifkey.mapiuid = lpvue->mapiuidNotif; ulNotifFlags = 0; (void) HrNotify((LPNOTIFKEY) &vuenotifkey, 1, ¬if, &ulNotifFlags); // Free the notification's copy of the modified row ScFreeBuffer(lpvue, notif.info.tab.row.lpProps); } } UnlockObj(lptad); return hrSuccess; #endif // NOTIFICATIONS return(ResultFromScode(MAPI_E_NO_SUPPORT)); } /*============================================================================ - TAD::HrInsertRow() - */ STDMETHODIMP TAD_HrInsertRow( LPTAD lptad, ULONG uliRow, LPSRow lprow ) { LPSRow lprowCopy = NULL; SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 }; ULONG cTagsAdded = 0; LPSRow * plprow; SCODE sc; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lptad,TAD_,HrInsertRow,lpVtbl); // lprow is validated by ScCopyTadRow() if (uliRow > lptad->ulcRowsAdd) { DebugTrace(TEXT("TAD::HrInsertRow() - Invalid parameter(s) passed\n") ); DebugTrace(TEXT("TAD::HrInsertRow() - uliRow is zero or greater thna row count\n") ); return ResultFromScode(MAPI_E_INVALID_PARAMETER); } #endif LockObj(lptad); // Make a copy of the row for our own use filtering // out PT_ERROR and PT_NULL columns and moving // the index column to the front if ( FAILED(sc = ScCopyTadRow(lptad, lprow, &cTagsAdded, &lprowCopy)) ) { DebugTrace(TEXT("TAD::HrInsertRow() - Error duping row to modify\n") ); goto ret; } sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol; sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND; // Find out where the row would collate on the Index sorted set // NOTE! We collate before the first occurance so that we will end // up pointing at the row with this index if it exists. plprow = PlprowCollateRow(lptad->ulcRowsIndex, lptad->parglprowIndex, (LPSSortOrderSet) &sosIndex, FALSE, lprowCopy); // If a row with the same Index value exists then we can't insert! if ( lptad->ulcRowsIndex && (plprow < (lptad->parglprowIndex + lptad->ulcRowsIndex)) && !LPropCompareProp( lprowCopy->lpProps , (*plprow)->lpProps)) { sc = MAPI_E_INVALID_PARAMETER; goto err; } // Insert it to the end of the unsorted row set if ( FAILED(sc = ScAddRow((LPUNKOBJ) lptad, NULL, // No sort order lprowCopy, uliRow, &lptad->ulcRowsAdd, &lptad->ulcRowMacAdd, &lptad->parglprowAdd, NULL)) ) { DebugTrace(TEXT("TAD::ScInsertRow() - Error appending new row to TAD (SCODE = 0x%08lX)\n"), sc ); goto err; } // Insert the row into the Index sorted row set if ( FAILED(sc = ScAddRow((LPUNKOBJ) lptad, NULL, // We already collated the row lprowCopy, (ULONG) (plprow - lptad->parglprowIndex), &lptad->ulcRowsIndex, &lptad->ulcRowMacIndex, &lptad->parglprowIndex, NULL)) ) { DebugTrace(TEXT("TAD::ScInsertRow() - Error appending new row to TAD (SCODE = 0x%08lX)\n"), sc ); goto err; } UpdateViews(lptad, 0, NULL, 1, &lprowCopy, &lprowCopy); ret: UnlockObj(lptad); return ResultFromScode(sc); err: // Reset the TAD columns lptad->lpptaCols->cValues -= cTagsAdded; goto ret; } /*============================================================================ - ScCopyTadRowSet() - * Validates and makes a copy of the specified row set using MAPI memory * allocation filtering out PT_ERROR and PT_NULL columns and moving the * index column to the first column. * * if lplpptaNew is not NULL then a list of columns which are new to * the TAD are returned. * * * Parameters: * lptad in Table data object * lprowsetIn in Row set to copy * pcNewTags out Number of columns new to the TAD * pcRows out Count of rows in the two row sets * pparglprowUnsortedCopy out Pointer to copied rows (Unsorted) * pparglprowSortedCopy out Pointer to copied rows (Sorted on Index) */ SCODE ScCopyTadRowSet( LPTAD lptad, LPSRowSet lprowsetIn, ULONG * pcNewTags, ULONG * pcRows, LPSRow * * pparglprowUnsortedCopy, LPSRow * * pparglprowSortedCopy ) { SCODE sc = S_OK; ULONG cRows = 0; LPSRow lprowIn; LPSRow lprowCopy = NULL; ULONG ulcRowsCopy = 0; ULONG ulcRowsMacCopy; LPSRow FAR * parglprowSortedCopy = NULL; LPSRow FAR * parglprowUnsortedCopy = NULL; ULONG cNewTags = 0; SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 }; // Assert Itable internal parameters are valid. Assert( !pcNewTags || !IsBadWritePtr( pcNewTags, sizeof(ULONG))); Assert( !IsBadWritePtr( pcRows, sizeof(ULONG))); Assert( !IsBadWritePtr( pparglprowUnsortedCopy, sizeof(LPSRow *))); Assert( !IsBadWritePtr( pparglprowSortedCopy, sizeof(LPSRow *))); // Validate Itable API parameters (ie lprowsetIn) // Note! The actual rows will be validated by ScCopyTadRow later. if ( IsBadReadPtr( lprowsetIn, sizeof(SRowSet)) || IsBadReadPtr( lprowsetIn->aRow , (UINT) (lprowsetIn->cRows * sizeof(SRow)))) { DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Bad row set In!\n") ); return MAPI_E_INVALID_PARAMETER; } // Allocate space the Index sorted list of copied rows if ( FAILED(sc = ScAllocateBuffer( lptad, sizeof(LPSRow) * lprowsetIn->cRows, &parglprowSortedCopy)) ) { DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error allocating parglprowSortedCopy (SCODE = 0x%08lX)\n"), sc ); goto err; } MAPISetBufferName(parglprowSortedCopy, TEXT("ITable copied Index sorted row list")); // Allocate space the unsorted list of copied rows if ( FAILED(sc = ScAllocateBuffer( lptad, sizeof(LPSRow) * lprowsetIn->cRows, &parglprowUnsortedCopy)) ) { DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error allocating parglprowUnsortedCopy (SCODE = 0x%08lX)\n"), sc ); goto err; } MAPISetBufferName(parglprowUnsortedCopy, TEXT("ITable copied unsorted row list")); // Set each LPSRow to NULL so we can easily free on error ZeroMemory( parglprowSortedCopy, (UINT) (sizeof(LPSRow) * lprowsetIn->cRows)); ZeroMemory( parglprowUnsortedCopy, (UINT) (sizeof(LPSRow) * lprowsetIn->cRows)); // Sort the copied rows by IndexCol. The code to add the rows // to the TAD relies on this (for speed). This code relies on the sort // to find duplicate index columns. sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol; sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND; // Mark our temporary Index Sorted Set as empty. ulcRowsCopy = 0; ulcRowsMacCopy = lprowsetIn->cRows; for ( lprowIn = lprowsetIn->aRow, cRows = ulcRowsMacCopy ; cRows ; lprowIn++, cRows--) { LPSRow * plprow; ULONG cTagsAdded = 0; // Make a copy of the row for our own use filtering // out PT_ERROR and PT_NULL columns and moving // the index column to the front // // Also adds any new tags to the TADs column set; if ( FAILED(sc = ScCopyTadRow( lptad , lprowIn , (pcNewTags) ? &cTagsAdded : NULL , &lprowCopy)) ) { DebugTrace(TEXT("TAD::HrInsertRow() - Error duping row\n") ); goto err; } cNewTags += cTagsAdded; // Find out where the row would collate in our Index sorted copy // NOTE! We collate before the first occurance so that we will end // up pointing at the row with this index if it exists. plprow = PlprowCollateRow( ulcRowsCopy , parglprowSortedCopy , (LPSSortOrderSet) &sosIndex , FALSE , lprowCopy); // If a row with the same Index value exists then we can't insert! if ( ulcRowsCopy && (plprow < (parglprowSortedCopy + ulcRowsCopy)) && !LPropCompareProp( lprowCopy->lpProps , (*plprow)->lpProps)) { sc = MAPI_E_INVALID_PARAMETER; DebugTrace(TEXT("TAD::ScCopyTadRowSet() - The same row index occurred on more than one row.\n") ); goto err; } // Append the row to the Unsorted row set // This is done before inserting it into the Index Sorted set to make // sure ulcRowsCopy is not incremented parglprowUnsortedCopy[ulcRowsCopy] = lprowCopy; // Insert the row into the Index sorted row set last if ( FAILED(sc = ScAddRow( (LPUNKOBJ) lptad , NULL // We already collated the row , lprowCopy , (ULONG) (plprow - parglprowSortedCopy) , &ulcRowsCopy , &ulcRowsMacCopy , &parglprowSortedCopy , NULL)) ) { DebugTrace(TEXT("TAD::ScCopyTadRowSet() - Error appending new row to RowSet copy (SCODE = 0x%08lX)\n"), sc ); goto err; } lprowCopy = NULL; } *pparglprowUnsortedCopy = parglprowUnsortedCopy; *pparglprowSortedCopy = parglprowSortedCopy; *pcRows = ulcRowsCopy; if (pcNewTags) { *pcNewTags = cNewTags; } ret: return sc; err: // Reset the TAD columns lptad->lpptaCols->cValues -= cNewTags; // We loop through the SORTED row set to free rows because we know that // if lprowCopy is not NULL then it hasn't been added to the SORTED set. // This prevents a double FreeBuffer on lprowCopy! if (parglprowSortedCopy) { LPSRow * plprowTmp = parglprowSortedCopy; while (ulcRowsCopy) { ScFreeBuffer( lptad, *(plprowTmp++)); ulcRowsCopy--; } ScFreeBuffer( lptad, parglprowSortedCopy); } ScFreeBuffer( lptad, parglprowUnsortedCopy); ScFreeBuffer( lptad, lprowCopy); goto ret; } /*============================================================================ - ScCopyTadRow() - * Validates and makes a copy of the specified row using MAPI memory * allocation filtering out PT_ERROR and PT_NULL columns and moving the * index column to the first column. * * Iff lpcNewTags is not NULL then any new columns are added to the END * of TAD's column set and the count of columns added is returned in * *lpcNewTags. Tags are added to the end so that the caller can * back out changes if necessary. * * Note! Unlike ScCopyVueROW it is the SRow structure that is * allocated and which must be subsequently freed to free the row. * * Parameters: * lptad in Table data object * lprow in Row set to copy * lpcTagsAdded out Number of columns added to the TAD's col set * lplprow out Pointer to copied row */ SCODE ScCopyTadRow( LPTAD lptad, LPSRow lprow, ULONG FAR * lpcTagsAdded, LPSRow FAR * lplprowCopy ) { ULONG ulcCols = 0; LONG iIndexCol; CMB cmb; SPropValue propTmp; LPSPropValue lppropDst; LPSPropValue lppropSrc; ULONG cTagsAdded = 0; LPSRow lprowCopy = NULL; SCODE sc = S_OK; Assert( !lpcTagsAdded || !IsBadWritePtr( lpcTagsAdded, sizeof(ULONG))); Assert( !IsBadWritePtr( lplprowCopy, sizeof(LPSRow))); // Validate the input row structure if (FBadRow( lprow)) { sc = MAPI_E_INVALID_PARAMETER; DebugTrace(TEXT("ScCopyTadRow() - Bad input row\n") ); return sc; } // The CMB (CopyMore Buffer) is used so that we do a single MAPI allocation // for PropCopyMore to use. It is used in conjuntion with the very // special ScBufAllocateMore to keep track of the chunks of memory which // would have otherwise been allocated with MAPI - AllocateMore. ZeroMemory(&cmb, sizeof(CMB)); // Figure out how many columns to copy and how much // additional memory they'll need to be copied. iIndexCol = -1; for ( lppropSrc = lprow->lpProps; lppropSrc < lprow->lpProps + lprow->cValues; lppropSrc++ ) { // Ignore PT_ERROR and PT_NULL properties if ( PROP_TYPE(lppropSrc->ulPropTag) == PT_ERROR || PROP_TYPE(lppropSrc->ulPropTag) == PT_NULL ) continue; // If this column is the index column, remember its // location in the copied (dst) row // so it can be moved to the first column in the copy if ( lppropSrc->ulPropTag == lptad->ulPropTagIndexCol ) iIndexCol = ulcCols; // If it's a new property and the caller asked us to (lpcTagsAdded) // add the tag to the TAD's column set else if ( lpcTagsAdded && !FFindColumn( lptad->lpptaCols, lppropSrc->ulPropTag)) { // Realloc the column only if there is no room if (lptad->lpptaCols->cValues >= lptad->ulcColsMac) { sc = ScCOReallocate( lptad , CbNewSPropTagArray( lptad->ulcColsMac + COLUMN_CHUNK_SIZE) , &lptad->lpptaCols); if (FAILED(sc)) { DebugTrace(TEXT("TAD::ScCopyTadRow() - Error resizing default column set (SCODE = 0x%08lX)\n"), sc ); goto err; } lptad->ulcColsMac += COLUMN_CHUNK_SIZE; } // Add the column to the end of the existing column set lptad->lpptaCols->aulPropTag[lptad->lpptaCols->cValues++] = lppropSrc->ulPropTag; cTagsAdded++; } // Add in the size of the column cmb.ulcb += UlcbPropToCopy(lppropSrc); ++ulcCols; } // Make sure the row to copy had an index column if ( iIndexCol == -1 ) { DebugTrace(TEXT("TAD::ScCopyTadRow() - Row doesn't have an index column!\n") ); sc = MAPI_E_INVALID_PARAMETER; goto err; } // Allocate space for the entire row (including all allocated values) if ( FAILED(sc = ScAllocateBuffer( lptad, sizeof(SRow) + 4 + // +4 to start lpProp at 8-byte bndry ulcCols * sizeof(SPropValue) + cmb.ulcb, &lprowCopy)) ) { DebugTrace(TEXT("TAD::ScCopyTadRow() - Error allocating row copy (SCODE = 0x%08lX)\n"), sc ); goto err; } MAPISetBufferName(lprowCopy, TEXT("ITable copy of entire row")); // Fill in the allocated SRow structure // WARNING! The allocate SRow structre MUST always be the first // structure in the memory allocation and the property // array MUST always immediately follow the SRow structure! // Itable code which passes internal rows around counts on // this. lprowCopy->cValues = ulcCols; lprowCopy->lpProps = (LPSPropValue)(((LPBYTE)lprowCopy) + sizeof(SRow)+4); // Set the initial pointer in our special CopyMoreBuffer. This buffer // will be used by our special AllocateMore routine to allocate // strings, bins, etc in PropCopyMore below. cmb.lpv = lprowCopy->lpProps + ulcCols; // Copy the row properties lppropDst = lprowCopy->lpProps + ulcCols; lppropSrc = lprow->lpProps + lprow->cValues; while ( lppropSrc-- > lprow->lpProps ) { // Strip out properties of type PT_ERROR and PT_NULL if ( PROP_TYPE(lppropSrc->ulPropTag) == PT_ERROR || PROP_TYPE(lppropSrc->ulPropTag) == PT_NULL ) { continue; } // Copy the property SideAssert( PropCopyMore( --lppropDst , lppropSrc , (LPALLOCATEMORE) ScBufAllocateMore , &cmb) == S_OK ); } // Move the index column to the front propTmp = *(lprowCopy->lpProps); *(lprowCopy->lpProps) = lprowCopy->lpProps[iIndexCol]; lprowCopy->lpProps[iIndexCol] = propTmp; // Return the copied row and the count of new properties *lplprowCopy = lprowCopy; if (lpcTagsAdded) { *lpcTagsAdded = cTagsAdded; } ret: return sc; err: // Reset the TAD columns lptad->lpptaCols->cValues -= cTagsAdded; ScFreeBuffer( lptad, lprowCopy); goto ret; } /*============================================================================ - UpdateViews() - * Updates all the views on a particular table data. For each view, if * lprowToRemove is non-NULL and present in the view, it is removed * from the view. If lprowToAdd is non-NULL and satisfies the current * restriction on the view, it is added to the view at the position * dictated by the view's current sort order. * * If the row to remove is bookmarked, the bookmark is moved to the next * row. * * OOM errors in adding a row to a view's row list are ignored; the view * simply doesn't see the new row. * * * Parameters: * lptad in TAD containing views to update * cRowsToRemove in Count of rows to remove from each view * parglprowToRemove in Array of LPSRows to remove from each view * cRowsToAdd in Count of rows to to each view * parglprowToAddUnsorted in Unsorted array of LPSRows to add to each view * parglprowToAddSorted in Sorted array of LPSRows to add to each view */ VOID UpdateViews( LPTAD lptad, ULONG cRowsToRemove, LPSRow * parglprowToRemove, ULONG cRowsToAdd, LPSRow * parglprowToAddUnsorted, LPSRow * parglprowToAddSorted ) { LPVUE lpvue; // This is an internal call which assumes that lptad is locked. for ( lpvue = (LPVUE) lptad->lpvueList; lpvue != NULL; lpvue = (LPVUE) lpvue->lpvueNext ) { AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE)) , TEXT("Bad lpvue in TAD vue List.") ); FixupView( lpvue , cRowsToRemove, parglprowToRemove , cRowsToAdd , parglprowToAddUnsorted , parglprowToAddSorted); } } /*============================================================================ - FixupView() - * Updates one view on a particular table data. Each row in * parglprowToRemove that is present in the view is removed * from the view. Each row in parglprowToAdd that satisfies the current * restriction on the view, is added to the view at the position * dictated by the view's current sort order. * * If the row to remove is bookmarked, the bookmark is moved to the next * row. * * OOM errors in adding a row to a view's row list are ignored; the view * simply doesn't see the new row. * * * Parameters: * lpvue in View to fixup * cRowsToRemove in Count of rows to remove from view * parglprowToRemove in Array of LPSRows to remove from view * cRowsToAdd in Count of rows to to view * parglprowToAddUnsorted in Unsorted array of LPSRows to add to view * parglprowToAddSorted in Sorted array of LPSRows to add to view */ VOID FixupView( LPVUE lpvue, ULONG cRowsToRemove, LPSRow * parglprowToRemove, ULONG cRowsToAdd, LPSRow * parglprowToAddUnsorted, LPSRow * parglprowToAddSorted ) { BOOL fBatchNotifs = TRUE; ULONG cNotifs = 0; ULONG ulcRows; NOTIFICATION argnotifBatch[MAX_BATCHED_NOTIFS]; SizedSSortOrderSet( 1, sosIndex) = { 1, 0, 0 }; LPNOTIFICATION lpnotif; PBK pbk; SCODE sc; Assert( !cRowsToRemove || !IsBadReadPtr( parglprowToRemove , (UINT) (cRowsToRemove * sizeof(LPSRow)))); Assert( !cRowsToAdd || !IsBadReadPtr( parglprowToAddUnsorted , (UINT) (cRowsToAdd * sizeof(LPSRow)))); Assert( !cRowsToAdd || !IsBadReadPtr( parglprowToAddSorted , (UINT) (cRowsToAdd * sizeof(LPSRow)))); if (!cRowsToRemove && !cRowsToAdd) { // Nothing to do in this case goto ret; } // This is an internal call which assumes that lptad is locked. // Set up a an Index sort order so that when we are checking to see // if a bookmark is deleted or changed we can search the SORTED row // row set using a binary search. sosIndex.aSort[0].ulPropTag = lpvue->lptadParent->ulPropTagIndexCol; sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND; // Mark bookmarks as moving or changed // This checks all bookmarks including BOOKMARK_CURRENT and BOOKMARK_END // BOOKMARK_BEGINNING is not checked or touched // Note that even though BOOKMARK_END is checked, it is not changed. pbk = lpvue->rgbk + cBookmarksMax; // Rememeber the number of rows in the view so we can fixup bkEnd ulcRows = lpvue->bkEnd.uliRow; while ( --pbk > lpvue->rgbk ) { LPSRow * plprowBk; LPSRow lprowBk; ULONG uliRow; ULONG fRowReplaced = FALSE; // If it's not a valid bookmark, don't update it. if ( !(pbk->dwfBKS & dwfBKSValid) || (pbk->dwfBKS & dwfBKSStale) ) { continue; } // Moving bookmarks always point to the actual row // Get a row index for moving bookmarks if (pbk->dwfBKS & dwfBKSMoving) { plprowBk = PlprowByLprow( ulcRows , lpvue->parglprows , pbk->lprow); AssertSz( plprowBk , TEXT("FixupViews() - Moving Bookmark lost it's row\n")); uliRow = (ULONG) (plprowBk - lpvue->parglprows); } else if (!ulcRows && !pbk->uliRow) { continue; } else if ((uliRow = pbk->uliRow) >= ulcRows) { // Bookmark is at the end of the table. Make sure it stays there pbk->uliRow += cRowsToAdd; continue; } Assert(uliRow < ulcRows); lprowBk = lpvue->parglprows[uliRow]; // If a row is on the "Remove" list then it may end up // moving or changed depending on whether it is also on the // "Add" list. if ( cRowsToRemove && (plprowBk = PlprowByLprow( cRowsToRemove , parglprowToRemove , lprowBk))) { // If a deleted row is on the "Add" list then it is marked as // moving and it is pointed (->lprow) at the added row. if ( cRowsToAdd && (plprowBk = PlprowCollateRow( cRowsToAdd , parglprowToAddSorted , (LPSSortOrderSet) &sosIndex , FALSE , lprowBk)) && (plprowBk < (parglprowToAddSorted + cRowsToAdd)) && !LPropCompareProp( lprowBk->lpProps , (*plprowBk)->lpProps)) { // Row is being replaced // Check to see if the row satisfies the specified restriction if ( FAILED(sc = ScSatisfiesRestriction( *plprowBk , lpvue->lpres , &fRowReplaced)) ) { DebugTrace(TEXT("FixupView() - Error evaluating restriction (SCODE = 0x%08lX)\n"), sc ); goto ret; } // If it doesn't, return now. if ( fRowReplaced ) { pbk->lprow = *plprowBk; pbk->dwfBKS = dwfBKSMoving | dwfBKSValid; } } // If a deleted row is not going to be listed then its bookmark // is "Changed". if (!fRowReplaced) { // Marked row was deleted. pbk->uliRow = uliRow; pbk->dwfBKS = dwfBKSChanged | dwfBKSValid; } } // If the row is not on the deletion list then it is automatically // marked as moving. else { // Marked row may move pbk->lprow = lprowBk; pbk->dwfBKS = dwfBKSMoving | dwfBKSValid; } } // Restore bkEnd lpvue->bkEnd.uliRow = ulcRows; // Remove rows from the view for ( ; cRowsToRemove; parglprowToRemove++, cRowsToRemove--) { LPSRow lprowRemove; LPSRow * plprowRemove; LPSRow * plprowAdd; BOOL fCanReplace = FALSE; ULONG uliRowAddDefault; // If the REMOVED row is not in the view then there is nothing to do if ( !*parglprowToRemove || !(plprowRemove = PlprowByLprow( lpvue->bkEnd.uliRow , lpvue->parglprows , *parglprowToRemove)) ) { continue; } // We will need this to fill in the notificaton later lprowRemove = *plprowRemove; // Go ahead and delete the current row from the VUE but remember // where it came from (uliRowAddDefault) so that if there is a // replacement and no sort order the replacement can be put back // into the same place uliRowAddDefault = (ULONG) (plprowRemove - lpvue->parglprows); MoveMemory( plprowRemove , plprowRemove + 1 , (size_t) (lpvue->bkEnd.uliRow - uliRowAddDefault - 1) * sizeof(LPSRow)); lpvue->bkEnd.uliRow -= 1; // See if the deleted row can be replaced by one // that is being added. For this to be TRUE: // There must be a Row with the same Index in the "Add" list // The row in the "Add" list must satisfy the VUE's restriction if ( (plprowAdd = PlprowCollateRow( cRowsToAdd , parglprowToAddSorted , (LPSSortOrderSet) &sosIndex , FALSE , lprowRemove)) && (plprowAdd < (parglprowToAddSorted + cRowsToAdd)) && !LPropCompareProp( (lprowRemove)->lpProps , (*plprowAdd)->lpProps) ) { // If the row to add satisifies the current restriction, // add it to the view according to the sort order or // the default location. if ( FAILED(sc = ScMaybeAddRow( lpvue , lpvue->lpres , lpvue->lpsos , *plprowAdd , uliRowAddDefault , &lpvue->bkEnd.uliRow , &lpvue->ulcRowMac , &lpvue->parglprows , &plprowAdd)) ) { DebugTrace(TEXT("TAD::FixupViews() - Error replacing row in VUE (SCODE = 0x%08lX)\n"), sc ); goto ret; } } // Make sure that we don't have more than MAX_BATCHED_NOTIFS // and that there is no replacment row if (!fBatchNotifs || (cNotifs >= MAX_BATCHED_NOTIFS)) { fBatchNotifs = FALSE; continue; } // Init a new notificaton lpnotif = argnotifBatch + cNotifs++; ZeroMemory(lpnotif, sizeof(NOTIFICATION)); lpnotif->ulEventType = fnevTableModified; // If row was deleted and added back... if ( (plprowAdd >= lpvue->parglprows) && (plprowAdd < (lpvue->parglprows + lpvue->bkEnd.uliRow))) { // Fill in a HACKED MODIFIED notificaion lpnotif->info.tab.ulTableEvent = TABLE_ROW_MODIFIED; // Use the notifs row structure to TEMPORARILY store // a pointer to the replacement row. lpnotif->info.tab.row.lpProps = (LPSPropValue) (*plprowAdd); } // ...else row was deleted and NOT added back. else { // Fill in a DELETE notification lpnotif->info.tab.ulTableEvent = TABLE_ROW_DELETED; lpnotif->info.tab.propIndex = *(lprowRemove->lpProps); lpnotif->info.tab.propPrior.ulPropTag = PR_NULL; } } // Add new rows to the table. This is done in the UNSORTED order // in case there is no VUE sort order for ( ; cRowsToAdd; parglprowToAddUnsorted++, cRowsToAdd--) { LPSRow * plprowAdd; // If the row has already been added then there is nothing to do. if ( *parglprowToAddUnsorted && (plprowAdd = PlprowByLprow( lpvue->bkEnd.uliRow , lpvue->parglprows , *parglprowToAddUnsorted)) ) { continue; } // If the row to add satisifies the current restriction, // add it to the view according to the sort order or // to the end of the table if no sort order is applied. if ( FAILED(sc = ScMaybeAddRow( lpvue , lpvue->lpres , lpvue->lpsos , *parglprowToAddUnsorted , lpvue->bkEnd.uliRow , &lpvue->bkEnd.uliRow , &lpvue->ulcRowMac , &lpvue->parglprows , &plprowAdd)) ) { DebugTrace(TEXT("TAD::FixupViews() - Error adding row to VUE (SCODE = 0x%08lX)\n"), sc ); goto ret; } if (!plprowAdd) { // Row was not added so don't fill out a notification continue; } // Make sure that we don't have more than MAX_BATCHED_NOTIFS // and that there is no replacment row if (!fBatchNotifs || (cNotifs >= MAX_BATCHED_NOTIFS)) { fBatchNotifs = FALSE; continue; } // Fill in a HACKED ADDED notificaion lpnotif = argnotifBatch + cNotifs++; ZeroMemory(lpnotif, sizeof(NOTIFICATION)); lpnotif->ulEventType = fnevTableModified; lpnotif->info.tab.ulTableEvent = TABLE_ROW_ADDED; // Use the notifs row structure to TEMPORARILY store // a pointer to the replacement row. lpnotif->info.tab.row.lpProps = (LPSPropValue) (*plprowAdd); } // If there too many notifications to batch then fill out a single // TABLE_CHANGED notification... if (!fBatchNotifs) { cNotifs = 1; lpnotif = argnotifBatch; ZeroMemory(lpnotif, sizeof(NOTIFICATION)); lpnotif->ulEventType = fnevTableModified; lpnotif->info.tab.ulTableEvent = TABLE_CHANGED; lpnotif->info.tab.propIndex.ulPropTag = lpnotif->info.tab.propPrior.ulPropTag = PR_NULL; } // ...else go through the batch of notifications and fixup // the ROW_ADDED and ROW_MODIFIED entries. else { LPSRow * plprowNotif; // Raid: Horsefly/Exchange/36281 // // The code above which fills in argnotifBatch doesn't necessarily // fill in the notifications in an order that can be processed // from first to last, which is a requirement for batched // notifications. As a workaround, the maximum number of // notifications in a batch was changed to 1 (MAX_BATCHED_NOTIFS // in _itable.h) so that order is not a problem. Should that // ever change to something other than 1, this bug will have to // be revisited a well as a crash below where ScFreeBuffer() // in the cleanup can end up clobbering a VUE's copy of the row // data if ScCopyVueRow() fails. See comment about filling in // TEMPORARY pointer to replacement row above. // AssertSz( cNotifs < 2, TEXT("Batch notifications of more than 1 not supported") ); for (lpnotif = argnotifBatch + cNotifs; lpnotif-- > argnotifBatch; ) { Assert( (lpnotif->ulEventType == fnevTableModified) && ( (lpnotif->info.tab.ulTableEvent == TABLE_ROW_MODIFIED) || (lpnotif->info.tab.ulTableEvent == TABLE_ROW_DELETED) || (lpnotif->info.tab.ulTableEvent == TABLE_ROW_ADDED))); if (lpnotif->info.tab.ulTableEvent == TABLE_ROW_DELETED) { // DELETE notifications don't need to be fixed up continue; } plprowNotif = PlprowByLprow( lpvue->bkEnd.uliRow , lpvue->parglprows , (LPSRow) (lpnotif->info.tab.row.lpProps)); Assert(plprowNotif); lpnotif->info.tab.propIndex = *((*plprowNotif)->lpProps); if (plprowNotif > lpvue->parglprows) { lpnotif->info.tab.propPrior = *((*(plprowNotif - 1))->lpProps); } else { lpnotif->info.tab.propPrior.ulPropTag = PR_NULL; } // Fill in row added/modified using the column set // currently active on the view being notified if ( FAILED(sc = ScCopyVueRow( lpvue , lpvue->lpptaCols , *plprowNotif , &(lpnotif->info.tab.row)))) { DebugTrace(TEXT("TAD::UpdateViews() - Error copying row to view notify (SCODE = 0x%08lX)\n"), sc ); // If the row can't be copied, then just skip this view goto ret; } } } // If a rows were added, modified or deleted, send the notification #ifdef NOTIFICATIONS if ( cNotifs ) { VUENOTIFKEY vuenotifkey; ULONG ulNotifFlags = 0; // Kick off notifications to all the notifications open on the view vuenotifkey.ulcb = sizeof(MAPIUID); vuenotifkey.mapiuid = lpvue->mapiuidNotif; (void) HrNotify((LPNOTIFKEY) &vuenotifkey, cNotifs, argnotifBatch, &ulNotifFlags); } #endif // NOTIFICATIONS ret: // Always fixup bkCurrent before leaving if ( FBookMarkStale( lpvue, BOOKMARK_CURRENT) ) { TrapSz( TEXT("FixupViews() - BOOKMARK_CURRENT became bad.\n")); } if (lpvue->bkCurrent.uliRow > lpvue->bkEnd.uliRow) { lpvue->bkCurrent.uliRow = lpvue->bkEnd.uliRow; } for (lpnotif = argnotifBatch; cNotifs; lpnotif++, --cNotifs) { // Free the notification's copy of any added/modified row ScFreeBuffer(lpvue, lpnotif->info.tab.row.lpProps); } return; } /*============================================================================ - ScReplaceRows() - * Replaces the rows with indexes matching the indexes of the list * rows with the corresponding row from the list. The old row is then * added to the list of replaced (old) rows. * * If a listed row has no existing counterpart then it is added * to TAD's. There is no row added to the replaced row list in this case. * * If a row is added it is appended to the end of the unsorted row table * and collated (by IndexCol) into the Index sorted row table. * * Parameters: * lptad in Table data object * cRowsNew in Count of rows to modify/add * parglprowNew in List of rows to modify/add * pcRowsOld Out Pointer count of rows replaced * pparglprowOld out Pointer to list of rows replaced */ SCODE ScReplaceRows( LPTAD lptad, ULONG cRowsNew, LPSRow * parglprowNew, ULONG * pcRowsOld, LPSRow * * pparglprowOld ) { SCODE sc; LPSRow * plprowNew; LPSRow * plprowOld; LPSRow * parglprowOld = NULL; // Make sure the table doesn't grow too big. This is not an exact test // but will be reasonable in almost all cases. May fail when // NumberRowsAdded + NumberRowsDeleted > 64K if (HIWORD(lptad->ulcRowsAdd + cRowsNew) != 0) { sc = MAPI_E_TABLE_TOO_BIG; DebugTrace(TEXT("ScReplaceRows() - In memory table has > 32767 rows (SCODE = 0x%08lX)\n"), sc ); goto ret; } // Make sure the unsorted and the Index sorted row lists are big // enough to handle all of the new rows. // This is done first so that we won't fail after we start adding rows. if ((lptad->ulcRowsAdd + cRowsNew) >= lptad->ulcRowMacAdd) { ULONG ulcRowsToAdd; ulcRowsToAdd = (cRowsNew > ROW_CHUNK_SIZE) ? cRowsNew : ROW_CHUNK_SIZE; if (FAILED(sc = ScCOReallocate( lptad , (lptad->ulcRowMacAdd + ulcRowsToAdd) * sizeof(LPSRow) , (VOID **) (&lptad->parglprowAdd)))) { DebugTrace(TEXT("ScAddRows() - Error growing unsorted row set (SCODE = 0x%08lX)\n"), sc ); goto ret; } // Increment ulcRowMacAdd only AFTER successfull allocation lptad->ulcRowMacAdd += ulcRowsToAdd; } if ((lptad->ulcRowsIndex + cRowsNew) >= lptad->ulcRowMacIndex) { ULONG ulcRowsToAdd; ulcRowsToAdd = (cRowsNew > ROW_CHUNK_SIZE) ? cRowsNew : ROW_CHUNK_SIZE; if (FAILED(sc = ScCOReallocate( lptad , (lptad->ulcRowMacIndex + ulcRowsToAdd) * sizeof(LPSRow) , (VOID **) (&lptad->parglprowIndex)))) { DebugTrace(TEXT("ScAddRows() - Error growing Index sorted row set (SCODE = 0x%08lX)\n"), sc ); goto ret; } // Increment ulcRowMacIndex only AFTER successfull allocation lptad->ulcRowMacIndex += ulcRowsToAdd; } // Allocate the list of old rows now so we won't fail after we start // adding rows. if (FAILED(sc = ScAllocateBuffer( lptad , cRowsNew * sizeof(LPSRow) , &parglprowOld))) { DebugTrace(TEXT("ScAddRows() - Error creating old row list (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(parglprowOld, TEXT("ITable old row list (replace)")); // This routine is not allowed to fail after this point. plprowOld = parglprowOld; for (plprowNew = parglprowNew; cRowsNew; plprowNew++, cRowsNew--) { LPSRow * plprow = NULL; sc = ScFindRow(lptad, (*plprowNew)->lpProps, &plprow); if (sc == S_OK) { // Put the old row into the old row set *plprowOld = *plprow; // Replace the row in the Index sorted set *plprow = *plprowNew; // Replace the row in the unsorted row set if (plprow = PlprowByLprow( lptad->ulcRowsAdd , lptad->parglprowAdd , *plprowOld)) { *plprow = *plprowNew; } // If the row was in the Index sorted set it should always be in // the unsorted set Assert(plprow); // Point at the next available slot for old rows plprowOld++; } // ...else didn't find the row. else { // Insert the row into the Index sorted set sc = ScAddRow( (LPUNKOBJ) lptad , NULL // We already collated the row , *plprowNew , (plprow) ? (ULONG) (plprow - lptad->parglprowIndex) : 0 , &lptad->ulcRowsIndex , &lptad->ulcRowMacIndex , &lptad->parglprowIndex , NULL); AssertSz1( !FAILED(sc) , TEXT("TAD::ScReplaceRows() - Error adding row to Index sorted set (SCODE = 0x%08lX)\n") , sc); // Add the row to the end of the unsorted set Assert( !IsBadWritePtr( lptad->parglprowAdd + lptad->ulcRowsAdd , sizeof(*plprowNew))); lptad->parglprowAdd[lptad->ulcRowsAdd++] = *plprowNew; } } sc = S_OK; // ignore NOT_FOUND error (or Asserted errors). if (plprowOld > parglprowOld) { *pparglprowOld = parglprowOld; *pcRowsOld = (ULONG) (plprowOld - parglprowOld); } else { ScFreeBuffer(lptad, parglprowOld); *pparglprowOld = NULL; *pcRowsOld = 0; } ret: return sc; //err: // No need to free old rows here since call is not allowed to fail // after old row list is allocated! } /*============================================================================ - VUE::Release() - */ STDMETHODIMP_(ULONG) VUE_Release( LPVUE lpvue ) { LPTAD lptadParent; ULONG ulcRef; #if !defined(NO_VALIDATION) if ( BAD_STANDARD_OBJ(lpvue,VUE_,Release,lpVtbl)) { DebugTrace(TEXT("VUE::Release() - Bad parameter passed as VUE object\n") ); return !0; } #endif lptadParent = lpvue->lptadParent; LockObj(lptadParent); // If there is an instance left then release it ulcRef = lpvue->ulcRef; if (ulcRef != 0) ulcRef = --lpvue->ulcRef; // The object can only be destroyed if there is no instance and no // active Advise left. //$ We can use lpvue->lpAdviselist if we can depend on HrUnsubscribe //$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe if ( ulcRef == 0 && !lpvue->ulcAdvise ) { CALLERRELEASE FAR * lpfReleaseCallback = lpvue->lpfReleaseCallback; ULONG ulReleaseData = lpvue->ulReleaseData; LPVUE * plpvue; // Call the release callback. Leave our crit sect before // calling back to prevent deadlock. if (lpfReleaseCallback) { UnlockObj(lptadParent); lpfReleaseCallback(ulReleaseData, (LPTABLEDATA) lptadParent, (LPMAPITABLE) lpvue); LockObj(lptadParent); } // Search the linked list of VUEs in our parent TAD for this VUE. for ( plpvue = &(lptadParent->lpvueList) ; *plpvue ; plpvue = &((*plpvue)->lpvueNext)) { if (*plpvue == lpvue) break; } // If this VUE was in the list then UNLINK it and FREE it if (*plpvue) { // Unlink the VUE *plpvue = lpvue->lpvueNext; // Free resources used by the VUE ScFreeBuffer(lpvue, lpvue->lpptaCols); ScFreeBuffer(lpvue, lpvue->lpres); ScFreeBuffer(lpvue, lpvue->lpsos); COFree(lpvue, lpvue->parglprows); #ifdef NOTIFICATIONS DestroyAdviseList(&lpvue->lpAdviseList); #endif // NOTIFICATIONS UNKOBJ_Deinit((LPUNKOBJ) lpvue); ScFreeBuffer(lpvue, lpvue); // Unlock and Release the parent TAD // This must be done after ScFreeBuffer sinse *pinst may go // away UnlockObj(lptadParent); UlRelease(lptadParent); } else { DebugTrace(TEXT("VUE::Release() - Table VUE not linked to TAD")); // Just unlock the parent tad. We will leak an unlinked vue. UnlockObj(lptadParent); } } else { #if DEBUG if ( ulcRef == 0 && lpvue->ulcAdvise ) { DebugTrace(TEXT("VUE::Release() - Table VUE still has active Advise")); } #endif // DEBUG UnlockObj(lptadParent); } return ulcRef; } /*============================================================================ - VUE::Advise() - */ STDMETHODIMP VUE_Advise( LPVUE lpvue, ULONG ulEventMask, LPMAPIADVISESINK lpAdviseSink, ULONG FAR * lpulConnection) { #ifdef NOTIFICATIONS SCODE sc; VUENOTIFKEY vuenotifkey; #endif // NOTIFICATIONS #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,Advise,lpVtbl); Validate_IMAPITable_Advise( lpvue, ulEventMask, lpAdviseSink, lpulConnection); #endif #ifdef NOTIFICATIONS LockObj(lpvue->lptadParent); vuenotifkey.ulcb = sizeof(MAPIUID); vuenotifkey.mapiuid = lpvue->mapiuidNotif; if ( FAILED(sc = GetScode(HrSubscribe(&lpvue->lpAdviseList, (LPNOTIFKEY) &vuenotifkey, ulEventMask, lpAdviseSink, 0, lpulConnection))) ) { DebugTrace(TEXT("VUE::Advise() - Error subscribing notification (SCODE = 0x%08lX)\n"), sc ); goto ret; } //$ We don't need lpvue->ulcAdvise if we can depend on HrUnsubscribe //$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe ++lpvue->ulcAdvise; ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); #endif // NOTIFICATIONS return(ResultFromScode(MAPI_E_NO_SUPPORT)); } /*============================================================================ - VUE::Unadvise() - */ STDMETHODIMP VUE_Unadvise( LPVUE lpvue, ULONG ulConnection) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,Unadvise,lpVtbl); Validate_IMAPITable_Unadvise( lpvue, ulConnection ); #endif #ifdef NOTIFICATIONS LockObj(lpvue->lptadParent); if ( FAILED(sc = GetScode(HrUnsubscribe(&lpvue->lpAdviseList, ulConnection))) ) { DebugTrace(TEXT("VUE::Unadvise() - Error unsubscribing notification (SCODE = 0x%08lX)\n"), sc ); goto ret; } // Decrement our advise count. //$ We don't need lpvue->ulcAdvise if we can depend on HrUnsubscribe //$ leaving lpvue->lpAdviseList NULL after the last HrUnsubscribe if (lpvue->ulcAdvise) { --lpvue->ulcAdvise; } ret: UnlockObj(lpvue->lptadParent); return ResultFromScode(sc); #endif // NOTIFICATIONS return(ResultFromScode(MAPI_E_NO_SUPPORT)); } /*============================================================================ - VUE::GetStatus() - * Since TAD based IMAPITables don't do anything asynchronously, this * function always reports TBLSTAT_COMPLETE. */ STDMETHODIMP VUE_GetStatus( LPVUE lpvue, ULONG FAR * lpulTableStatus, ULONG FAR * lpulTableType ) { #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,GetStatus,lpVtbl); Validate_IMAPITable_GetStatus( lpvue, lpulTableStatus, lpulTableType ); #endif *lpulTableStatus = TBLSTAT_COMPLETE; *lpulTableType = lpvue->lptadParent->ulTableType; return HrSetLastErrorIds(lpvue, S_OK, 0); } /*============================================================================ - VUE::SetColumns() - * Replaces the current column set with a copy of the specified column set * and frees the old column set. */ STDMETHODIMP VUE_SetColumns( LPVUE lpvue, LPSPropTagArray lpptaCols, ULONG ulFlags ) { LPSPropTagArray lpptaColsCopy; SCODE sc; #if !defined(NO_VALIDATION) // VALIDATE_OBJ(lpvue,VUE_,SetColumns,lpVtbl); // Validate_IMAPITable_SetColumns( lpvue, lpptaCols, ulFlags ); // Commented by YST #endif LockObj(lpvue->lptadParent); // Copy the column set if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue , CbNewSPropTagArray(lpptaCols->cValues) , (LPBYTE) lpptaCols , 0 , (LPBYTE FAR *) &lpptaColsCopy)) ) { DebugTrace(TEXT("VUE::SetColumns() - Error duping column set (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(lpptaColsCopy, TEXT("ITable: dup column set")); // Replace the current column set with the copy and // free the old one. ScFreeBuffer(lpvue, lpvue->lpptaCols); lpvue->lpptaCols = lpptaColsCopy; // [PaulHi] 4/5/99 @comment Shouldn't // clients of the WAB request ANSI/UNICODE based on property tags in the // column array, rather than doing mass conversions? // Seems like the fix is to create two versions of column property arrays, // an ANSI and Unicode version. FixupColsWA(lpvue->lpptaCols, lpvue->bMAPIUnicodeTable); ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::QueryColumns() - * Returns a copy of the current column set or the available column set. */ STDMETHODIMP VUE_QueryColumns( LPVUE lpvue, ULONG ulFlags, LPSPropTagArray FAR * lplpptaCols ) { LPSPropTagArray lpptaCols; SCODE sc; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,QueryColumns,lpVtbl); Validate_IMAPITable_QueryColumns( lpvue, ulFlags, lplpptaCols ); #endif LockObj(lpvue->lptadParent); // Figure out which column set to return lpptaCols = (ulFlags & TBL_ALL_COLUMNS) ? lpvue->lptadParent->lpptaCols : lpvue->lpptaCols; // Return a copy of it to the caller if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue , CbNewSPropTagArray(lpptaCols->cValues) , (LPBYTE) lpptaCols , 0 , (LPBYTE FAR *) lplpptaCols)) ) { DebugTrace(TEXT("VUE::QueryColumns() - Error copying column set to return (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(*lplpptaCols, TEXT("ITable: QueryColumns column set")); ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::GetRowCount() - * Returns the count of rows in the table. */ STDMETHODIMP VUE_GetRowCount( LPVUE lpvue, ULONG ulFlags, ULONG FAR * lpulcRows ) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,GetRowCount,lpVtbl); Validate_IMAPITable_GetRowCount( lpvue, ulFlags, lpulcRows ); #endif LockObj(lpvue->lptadParent); *lpulcRows = lpvue->bkEnd.uliRow; UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::SeekRow() - * Seeks to the specified row in the table. */ STDMETHODIMP VUE_SeekRow( LPVUE lpvue, BOOKMARK bkOrigin, LONG lcRowsToSeek, LONG FAR * lplcRowsSought ) { LONG lcRowsSought; PBK pbk; SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,SeekRow,lpVtbl); Validate_IMAPITable_SeekRow( lpvue, bkOrigin, lcRowsToSeek, lplcRowsSought ); if ( FBadBookmark(lpvue,bkOrigin) || (lplcRowsSought && IsBadWritePtr(lplcRowsSought,sizeof(LONG))) ) { DebugTrace(TEXT("VUE::SeekRow() - Bad parameter(s) passed\n") ); return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0); } #endif LockObj(lpvue->lptadParent); // Validate the bookmark and adjust Moving and Changed bookmarks if ( FBookMarkStale( lpvue, bkOrigin) ) { sc = MAPI_E_INVALID_BOOKMARK; DebugTrace(TEXT("VUE::SeekRow() - Invalid bookmark (SCODE = 0x%08lX)\n"), sc ); goto ret; } // Do the seek pbk = lpvue->rgbk + bkOrigin; lcRowsSought = lcRowsToSeek < 0 ? -min((LONG) pbk->uliRow, -lcRowsToSeek) : min(lcRowsToSeek, (LONG)(lpvue->bkEnd.uliRow - pbk->uliRow)); lpvue->bkCurrent.uliRow = pbk->uliRow + lcRowsSought; // If caller wants to know how far we sought, fill it in if ( lplcRowsSought ) *lplcRowsSought = lcRowsSought; // Warn if the bookmark sought from refered to a different // row from the last time it was used if ( pbk->dwfBKS & dwfBKSChanged ) { pbk->dwfBKS &= ~dwfBKSChanged; // Warn only once sc = MAPI_W_POSITION_CHANGED; } ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::SeekRowApprox() - * Seeks to the approximate fractional position in the table. */ STDMETHODIMP VUE_SeekRowApprox( LPVUE lpvue, ULONG ulNumerator, ULONG ulDenominator ) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,SeekRowApprox,lpVtbl); Validate_IMAPITable_SeekRowApprox( lpvue, ulNumerator, ulDenominator ); #endif LockObj(lpvue->lptadParent); // Any fraction whose numerator is greater than or equal to its // denominator should be fixed up to be equivalent to ulcRows/ulcRows. // (i.e. Seek to the end of the table). Also, fixup denominator // so it's never 0 (which would crash the approximate position // calculation). if ( ulNumerator >= ulDenominator ) { ulDenominator = UlDenominator(lpvue->bkEnd.uliRow); ulNumerator = ulDenominator; } // Pare the approximate position down to 16-bit accuracy // (If someone wants to seek approximate to something that's accurate // to more than 1/32768th, tough!) while ( HIWORD(ulNumerator) != 0 ) { ulNumerator >>= 1; ulDenominator >>= 1; } // Assert that we have less than a word's worth of rows in the table. // (If someone wants > 32767 entries in an *IN MEMORY* table, tough!) AssertSz( HIWORD(lpvue->bkEnd.uliRow) == 0, TEXT("Table has more than 32767 rows. Can't be supported in memory.") ); // Set the position lpvue->bkCurrent.uliRow = lpvue->bkEnd.uliRow * ulNumerator / ulDenominator; UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::QueryPosition() - * Query the current exact and approximate fractional position in * the table. */ STDMETHODIMP VUE_QueryPosition( LPVUE lpvue, ULONG FAR * lpulRow, ULONG FAR * lpulNumerator, ULONG FAR * lpulDenominator ) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,QueryPosition,lpVtbl); Validate_IMAPITable_QueryPosition( lpvue, lpulRow, lpulNumerator, lpulDenominator); #endif LockObj(lpvue->lptadParent); *lpulRow = lpvue->bkCurrent.uliRow; *lpulNumerator = lpvue->bkCurrent.uliRow; *lpulDenominator = UlDenominator(lpvue->bkEnd.uliRow); UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::FindRow() - */ STDMETHODIMP VUE_FindRow( LPVUE lpvue, LPSRestriction lpres, BOOKMARK bkOrigin, ULONG ulFlags ) { PBK pbk; LPSRow * plprow; ULONG fSatisfies; SCODE sc; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,FindRow,lpVtbl); Validate_IMAPITable_FindRow( lpvue, lpres, bkOrigin, ulFlags ); if ( FBadBookmark(lpvue,bkOrigin) ) { DebugTrace(TEXT("VUE::FindRow() - Bad parameter(s) passed\n") ); return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0); } #endif LockObj(lpvue->lptadParent); // Validate the bookmark and adjust Moving and Changed bookmarks if ( FBookMarkStale( lpvue, bkOrigin) ) { sc = MAPI_E_INVALID_BOOKMARK; DebugTrace(TEXT("VUE::FindRow() - Invalid bookmark (SCODE = 0x%08lX)\n"), sc ); goto ret; } pbk = lpvue->rgbk + bkOrigin; plprow = lpvue->parglprows + pbk->uliRow; if ( ulFlags & DIR_BACKWARD ) { while ( plprow-- > lpvue->parglprows ) { if ( FAILED(sc = ScSatisfiesRestriction(*plprow, lpres, &fSatisfies)) ) { DebugTrace(TEXT("VUE::FindRow() - Error evaluating restriction on row (SCODE = 0x%08lX)\n"), sc ); goto ret; } if ( fSatisfies ) goto found_row; } } else { while ( plprow < lpvue->parglprows + lpvue->bkEnd.uliRow ) { if ( FAILED(sc = ScSatisfiesRestriction(*plprow, lpres, &fSatisfies )) ) { DebugTrace(TEXT("VUE::FindRow() - Error evaluating restriction on row (SCODE = 0x%08lX)\n"), sc ); goto ret; } if ( fSatisfies ) goto found_row; ++plprow; } } sc = MAPI_E_NOT_FOUND; goto ret; found_row: lpvue->bkCurrent.uliRow = (ULONG) (plprow - lpvue->parglprows); // Warn if the bookmark sought from refered to a different // row from the last time it was used if ( pbk->dwfBKS & dwfBKSChanged ) { pbk->dwfBKS &= ~dwfBKSChanged; // Warn only once sc = MAPI_W_POSITION_CHANGED; } ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - HrVUERestrict() - * 4/22/97 * This is basically the VUE_Restrict function isolated so that it can be * called from LDAPVUE_Restrict without any parameter validation * * */ HRESULT HrVUERestrict( LPVUE lpvue, LPSRestriction lpres, ULONG ulFlags ) { LPSRestriction lpresCopy; SCODE sc; LockObj(lpvue->lptadParent); // Make a copy of the restriction for our use if ( FAILED(sc = ScDupRestriction((LPUNKOBJ) lpvue, lpres, &lpresCopy)) ) { DebugTrace(TEXT("VUE::Restrict() - Error duping restriction (SCODE = 0x%08lX)\n"), sc ); goto ret; } // Build a new list of rows from the TAD which satisfy the new restriction if ( FAILED(sc = ScLoadRows(lpvue->lptadParent->ulcRowsAdd, lpvue->lptadParent->parglprowAdd, lpvue, lpresCopy, lpvue->lpsos)) ) { DebugTrace(TEXT("VUE::Restrict() - Error building restricted row set (SCODE = 0x%08lX)\n"), sc ); ScFreeBuffer(lpvue, lpresCopy); goto ret; } // Replace the old restriction with the new one ScFreeBuffer(lpvue, lpvue->lpres); lpvue->lpres = lpresCopy; ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::Restrict() - * Reloads the view with rows satisfying the new restriction. */ STDMETHODIMP VUE_Restrict( LPVUE lpvue, LPSRestriction lpres, ULONG ulFlags ) { #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,Restrict,lpVtbl); Validate_IMAPITable_Restrict( lpvue, lpres, ulFlags ); #endif return HrVUERestrict(lpvue, lpres, ulFlags); } /*============================================================================ - VUE::CreateBookmark() - */ STDMETHODIMP VUE_CreateBookmark( LPVUE lpvue, BOOKMARK FAR * lpbkPosition ) { PBK pbk; SCODE sc = S_OK; IDS ids = 0; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,CreateBookmark,lpVtbl); Validate_IMAPITable_CreateBookmark( lpvue, lpbkPosition); #endif LockObj(lpvue->lptadParent); if ( lpvue->bkCurrent.uliRow == lpvue->bkEnd.uliRow ) { // If we're at EOT, just return bkEnd *lpbkPosition = (BOOKMARK) BOOKMARK_END; } else { // Othewise, look for a free bookmark and return it pbk = lpvue->rgbk + cBookmarksMax; while ( pbk-- > lpvue->rgbk ) { if ( pbk->dwfBKS == dwfBKSFree ) { pbk->dwfBKS = dwfBKSValid; pbk->uliRow = lpvue->bkCurrent.uliRow; *lpbkPosition = (BOOKMARK)(pbk - lpvue->rgbk); goto ret; } } DebugTrace(TEXT("VUE::CreateBookmark() - Out of bookmarks\n") ); sc = MAPI_E_UNABLE_TO_COMPLETE; #ifdef OLD_STUFF ids = IDS_OUT_OF_BOOKMARKS; #endif // OLD_STUFF } ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, ids); } /*============================================================================ - VUE::FreeBookmark() - */ STDMETHODIMP VUE_FreeBookmark( LPVUE lpvue, BOOKMARK bkOrigin ) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,FreeBookmark,lpVtbl); Validate_IMAPITable_FreeBookmark( lpvue, bkOrigin ); if ( FBadBookmark(lpvue,bkOrigin) ) { DebugTrace(TEXT("VUE::FreeBookmark() - Bad parameter(s) passed\n") ); return HrSetLastErrorIds(lpvue, MAPI_E_INVALID_PARAMETER, 0); } #endif LockObj(lpvue->lptadParent); // Free the bookmark (ignoring predefined ones) if ( bkOrigin > cBookmarksReserved ) lpvue->rgbk[bkOrigin].dwfBKS = dwfBKSFree; UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::SortTable() - * Reloads the view with rows in the new sort order. Note that the * sort order may be NULL (since ITABLE.DLL allows creating tables * views NULL sort orders). * * //$??? While being minimal in code size, this approach is somewhat * //$??? slow having to reload the table. If it becomes a performance * //$??? issue, a sort function can be implemented instead.. */ STDMETHODIMP VUE_SortTable( LPVUE lpvue, LPSSortOrderSet lpsos, ULONG ulFlags ) { LPSSortOrderSet lpsosCopy = NULL; SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,SortTable,lpVtbl); Validate_IMAPITable_SortTable( lpvue, lpsos, ulFlags ); #endif if (lpsos && lpsos->cCategories) { DebugTrace(TEXT("VUE::SortTable() - VUE::SortTable doesn't support categories\n") ); #ifdef OLD_STUFF return HrSetLastErrorIds(lpvue, MAPI_E_TOO_COMPLEX, IDS_CANT_CATEGORIZE); #endif // OLD_STUFF return HrSetLastErrorIds(lpvue, MAPI_E_TOO_COMPLEX, 0); } LockObj(lpvue->lptadParent); // If the sort order is not empty then dup it... // adding the index column as the least significant sort if it's not // already there. if ( lpsos && lpsos->cSorts ) { LPSSortOrder lpsoIndex; UINT iSortIndex; ULONG ulTagIndex = lpvue->lptadParent->ulPropTagIndexCol; // Look to see if the index column is alread in the sort. for ( lpsoIndex = lpsos->aSort, iSortIndex = 0 ; iSortIndex < lpsos->cSorts ; iSortIndex++, lpsoIndex++) { if ( (lpsoIndex->ulPropTag == ulTagIndex) || ( (PROP_ID(lpsoIndex->ulPropTag) == PROP_ID(ulTagIndex)) && (PROP_TYPE(lpsoIndex->ulPropTag) == PT_UNSPECIFIED))) { break; } } // Make a copy of the sort order set for our own use if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue , CbSSortOrderSet(lpsos) , (LPBYTE) lpsos , (iSortIndex == lpsos->cSorts) ? sizeof(SSortOrder) : 0 , (LPBYTE FAR *) &lpsosCopy)) ) { DebugTrace(TEXT("VUE::SortTable() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(lpsosCopy, TEXT("ITable: SortTable SOS copy")); if (iSortIndex == lpsos->cSorts) { lpsosCopy->aSort[iSortIndex].ulPropTag = ulTagIndex; lpsosCopy->aSort[iSortIndex].ulOrder = TABLE_SORT_ASCEND; lpsosCopy->cSorts++; } } // ...else the lpsos is empty so we are already sorted else { // Put the old lpsos into lpsosCopy so that it will be freed. lpsosCopy = lpvue->lpsos; lpvue->lpsos = NULL; goto ret; } // Only do the sort if the new sort is NOT a proper subset of the new // sort. //$ Well... Now that we added the SECRET last sort, this optimization //$ is "almost" useless! if ( !lpvue->lpsos || lpsosCopy->cSorts > lpvue->lpsos->cSorts || memcmp( lpvue->lpsos->aSort , lpsosCopy->aSort , (UINT) (lpsosCopy->cSorts * sizeof(SSortOrder))) ) { // Sort the VUE rows into a new row set // NOTE! We use the rows from the existing VUE set in order to // take advantage of the fact the restriction is already // done. if ( FAILED(sc = ScLoadRows(lpvue->bkEnd.uliRow, lpvue->parglprows, lpvue, NULL, // The restriction is already done lpsosCopy)) ) { DebugTrace(TEXT("VUE::SortTable() - Building sorted row set (SCODE = 0x%08lX)\n"), sc ); goto ret; } } // Swap the old lpsos with lpsosCopy lpsos = lpvue->lpsos; lpvue->lpsos = lpsosCopy; lpsosCopy = lpsos; ret: // Free the left over SOS ScFreeBuffer(lpvue, lpsosCopy); UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::QuerySortOrder() - * Returns the current sort order which may be NULL since ITABLE.DLL * allows creation of views with NULL sort orders. */ STDMETHODIMP VUE_QuerySortOrder( LPVUE lpvue, LPSSortOrderSet FAR * lplpsos ) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,QuerySortOrder,lpVtbl); Validate_IMAPITable_QuerySortOrder( lpvue, lplpsos ); #endif LockObj(lpvue->lptadParent); if ( !lpvue->lpsos ) { UINT cb = CbNewSSortOrderSet(0); // allocate a sort order set containing zero sort orders if ( FAILED(sc = ScAllocateBuffer(lpvue, cb, (LPBYTE *) lplpsos))) { DebugTrace(TEXT("VUE::QuerySortOrder() - Error allocating SortOrderSet (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(*lplpsos, TEXT("ITable new sort order set")); // zero the sort order set - which sets the number of sort columns to zero ZeroMemory(*lplpsos, cb); } // Make a copy of our sort order set to return to the caller. else if ( FAILED(sc = ScDupRgbEx( (LPUNKOBJ) lpvue , CbSSortOrderSet(lpvue->lpsos) , (LPBYTE) (lpvue->lpsos) , 0 , (LPBYTE FAR *) lplpsos)) ) { DebugTrace(TEXT("VUE::QuerySortOrder() - Error duping sort order set (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(*lplpsos, TEXT("ITable: QuerySortOrder SOS")); ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - VUE::QueryRows() - */ STDMETHODIMP VUE_QueryRows( LPVUE lpvue, LONG lcRows, ULONG ulFlags, LPSRowSet FAR * lplprows ) { LPSRow * plprowSrc; LPSRow lprowDst; LPSRowSet lprows = NULL; SCODE sc; #define ABS(n) ((n) < 0 ? -1*(n) : (n)) #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,QueryRows,lpVtbl); #ifndef _WIN64 // Need to investigate more, why this is always fail (YST) Validate_IMAPITable_QueryRows( lpvue, lcRows, ulFlags, lplprows ); #endif // _WIN64 #endif LockObj(lpvue->lptadParent); // If querying backward, seek back as far as needed and read from there plprowSrc = lpvue->parglprows + lpvue->bkCurrent.uliRow; if ( lcRows < 0 ) { lcRows = -min((LONG)lpvue->bkCurrent.uliRow, -lcRows); plprowSrc += lcRows; } else { lcRows = min(lcRows, (LONG)(lpvue->bkEnd.uliRow - lpvue->bkCurrent.uliRow)); } // Allocate the row set if ( FAILED(sc = ScAllocateBuffer( lpvue, CbNewSRowSet(ABS(lcRows)), &lprows)) ) { DebugTrace(TEXT("VUE::QueryRows() - Error allocating row set (SCODE = 0x%08lX)\n"), sc ); goto err; } MAPISetBufferName(lprows, TEXT("ITable query rows")); // Copy the rows // Start with a count of zero rows so we end up with the correct number // on partial success lprows->cRows = 0; for ( lprowDst = lprows->aRow; lprowDst < lprows->aRow + ABS(lcRows); lprowDst++, plprowSrc++ ) { if ( FAILED(sc = ScCopyVueRow(lpvue, lpvue->lpptaCols, *plprowSrc, lprowDst)) ) { DebugTrace(TEXT("VUE::QueryRows() - Error copying row (SCODE = 0x%08lX)\n"), sc ); goto err; } lprows->cRows++; } if ( (lcRows >= 0 && !(ulFlags & TBL_NOADVANCE)) || (lcRows < 0 && (ulFlags & TBL_NOADVANCE)) ) { lpvue->bkCurrent.uliRow += lcRows; } *lplprows = lprows; ret: UnlockObj(lpvue->lptadParent); return HrSetLastErrorIds(lpvue, sc, 0); err: MAPIFreeRows(lpvue, lprows); goto ret; #undef ABS } /*============================================================================ - VUE::Abort() - */ STDMETHODIMP VUE_Abort( LPVUE lpvue ) { #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,Abort,lpVtbl); Validate_IMAPITable_Abort( lpvue ); #endif return HrSetLastErrorIds(lpvue, S_OK, 0); } STDMETHODIMP VUE_ExpandRow(LPVUE lpvue, ULONG cbIKey, LPBYTE pbIKey, ULONG ulRowCount, ULONG ulFlags, LPSRowSet FAR *lppRows, ULONG FAR *lpulMoreRows) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,ExpandRow,lpVtbl); Validate_IMAPITable_ExpandRow( lpvue, cbIKey, pbIKey, ulRowCount, ulFlags, lppRows, lpulMoreRows); #endif sc = MAPI_E_NO_SUPPORT; return HrSetLastErrorIds(lpvue, sc, 0); } STDMETHODIMP VUE_CollapseRow(LPVUE lpvue, ULONG cbIKey, LPBYTE pbIKey, ULONG ulFlags, ULONG FAR *lpulRowCount) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,CollapseRow,lpVtbl); Validate_IMAPITable_CollapseRow( lpvue, cbIKey, pbIKey, ulFlags, lpulRowCount); #endif sc = MAPI_E_NO_SUPPORT; return HrSetLastErrorIds(lpvue, sc, 0); } STDMETHODIMP VUE_WaitForCompletion(LPVUE lpvue, ULONG ulFlags, ULONG ulTimeout, ULONG FAR *lpulTableStatus) { SCODE sc = S_OK; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,WaitForCompletion,lpVtbl); Validate_IMAPITable_WaitForCompletion( lpvue, ulFlags, ulTimeout, lpulTableStatus); #endif if (lpulTableStatus) *lpulTableStatus = TBLSTAT_COMPLETE; return HrSetLastErrorIds(lpvue, sc, 0); } STDMETHODIMP VUE_GetCollapseState(LPVUE lpvue, ULONG ulFlags, ULONG cbInstanceKey, LPBYTE pbInstanceKey, ULONG FAR * lpcbCollapseState, LPBYTE FAR * lppbCollapseState) { SCODE sc = MAPI_E_NO_SUPPORT; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,GetCollapseState,lpVtbl); Validate_IMAPITable_GetCollapseState( lpvue, ulFlags, cbInstanceKey, pbInstanceKey, lpcbCollapseState, lppbCollapseState); #endif return HrSetLastErrorIds(lpvue, sc, 0); } STDMETHODIMP VUE_SetCollapseState(LPVUE lpvue, ULONG ulFlags, ULONG cbCollapseState, LPBYTE pbCollapseState, BOOKMARK FAR * lpbkLocation) { SCODE sc = MAPI_E_NO_SUPPORT; #if !defined(NO_VALIDATION) VALIDATE_OBJ(lpvue,VUE_,SetCollapseState,lpVtbl); Validate_IMAPITable_SetCollapseState( lpvue, ulFlags, cbCollapseState, pbCollapseState, lpbkLocation); #endif return HrSetLastErrorIds(lpvue, sc, 0); } /*============================================================================ - ScDeleteAllRows() - * Deletes all rows from a TAD and its views. * * Parameters: * lptad in TAD to delete all rows from */ SCODE ScDeleteAllRows( LPTAD lptad) { LPSRow * plprow; #ifdef NOTIFICATIONS LPVUE lpvue; #endif // NOTIFICATIONS NOTIFICATION notif; // Delete all rows from the unsorted set for ( plprow = lptad->parglprowAdd + lptad->ulcRowsAdd ; --plprow >= lptad->parglprowAdd ;) { Assert( plprow && *plprow); ScFreeBuffer( lptad, *plprow); } // Tell the TAD it has no rows left lptad->ulcRowsAdd = lptad->ulcRowsIndex = 0; // Set the constant portion of the notification ZeroMemory(¬if, sizeof(NOTIFICATION)); notif.ulEventType = fnevTableModified; notif.info.tab.ulTableEvent = TABLE_RELOAD; notif.info.tab.propIndex.ulPropTag = notif.info.tab.propPrior.ulPropTag = PR_NULL; #ifdef NOTIFICATIONS // Tell each view that it has no rows left for ( lpvue = (LPVUE) lptad->lpvueList; lpvue != NULL; lpvue = (LPVUE) lpvue->lpvueNext ) { VUENOTIFKEY vuenotifkey; ULONG ulNotifFlags = 0; AssertSz( !IsBadWritePtr(lpvue, sizeof(VUE)) && lpvue->lpVtbl == &vtblVUE , TEXT("Bad lpvue in TAD vue List.") ); // Reset all of the bookmarks // This automagically tells the view that it has no rows. ZeroMemory( lpvue->rgbk, cBookmarksMax * sizeof(BK)); // Initialize the predefined bookmarks lpvue->bkBeginning.dwfBKS = dwfBKSValid; lpvue->bkCurrent.dwfBKS = dwfBKSValid; lpvue->bkEnd.dwfBKS = dwfBKSValid; vuenotifkey.ulcb = sizeof(MAPIUID); vuenotifkey.mapiuid = lpvue->mapiuidNotif; (void) HrNotify((LPNOTIFKEY) &vuenotifkey, 1, ¬if, &ulNotifFlags); } #endif // NOTIFICATIONS // Currently this call cannot fail! return S_OK; } /*============================================================================ - ScLoadRows() - * Loads a view's row set with rows from the table data according * to the specified restriction and sort order and resets all bookmarks. * * Parameters: * lpvue in View whose row set is to be loaded * lpres in Restriction on loaded row set * lpsos in Sort order of loaded row set */ SCODE ScLoadRows( ULONG ulcRowsSrc, LPSRow * rglprowsSrc, LPVUE lpvue, LPSRestriction lpres, LPSSortOrderSet lpsos ) { LPTAD lptad = (LPTAD) lpvue->lptadParent; LPSRow * plprowSrc; LPSRow * plprowDst; PBK pbk; ULONG ulcRows = 0; ULONG ulcRowMac = 0; LPSRow * parglprows = NULL; SCODE sc = S_OK; // Iterate through the table data adding to the view any rows // which satisfy the specified restriction. Note, the forward // iteration is necessary here so that the rows load in order // when no sort order set is specified. for ( plprowSrc = rglprowsSrc; plprowSrc < rglprowsSrc + ulcRowsSrc; plprowSrc++ ) { // If the row satisfies the specified restriction, add it to the // row set updating bookmarks as necessary. if ( FAILED(sc = ScMaybeAddRow(lpvue, lpres, lpsos, *plprowSrc, ulcRows, &ulcRows, &ulcRowMac, &parglprows, &plprowDst)) ) { DebugTrace(TEXT("VUE::ScLoadRows() - Error adding row to view (SCODE = 0x%08lX)\n"), sc ); goto ret; } } // Replace the row set COFree(lpvue, lpvue->parglprows); lpvue->parglprows = parglprows; lpvue->ulcRowMac = ulcRowMac; lpvue->bkEnd.uliRow = ulcRows; // Lose all user-defined bookmarks, and reset BOOKMARK_CURRENT // to BOOKMARK_BEGINNING (i.e. 0) (Raid 1331) pbk = lpvue->rgbk + cBookmarksMax; while ( pbk-- > lpvue->rgbk + cBookmarksReserved ) if ( pbk->dwfBKS & dwfBKSValid ) pbk->dwfBKS = dwfBKSValid | dwfBKSStale; lpvue->bkCurrent.uliRow = 0; ret: return sc; } /*============================================================================ - ScMaybeAddRow() - * If the specified row satisfies the specified restriction, add it * to the row set of the specified view according to the specified * sort order returning a pointer to the location where the row was * added. * * * Parameters: * lpvue in VUE with instance variable containing * allocators. * lpres in Restriction row must satisfy to be added * lpsos in Sort order of row set. * lprow in Row to maybe add. * ulcRows in Count of rows in the row set. * pparglprows in/out Pointer to buffer containing row set. * pplprow out Pointer to location of added row in row set. * (Set to NULL if the row isn't added.) */ SCODE ScMaybeAddRow( LPVUE lpvue, LPSRestriction lpres, LPSSortOrderSet lpsos, LPSRow lprow, ULONG uliRow, ULONG * pulcRows, ULONG * pulcRowMac, LPSRow ** pparglprows, LPSRow ** pplprow ) { ULONG fSatisfies; SCODE sc; // Check to see if the row satisfies the specified restriction if ( FAILED(sc = ScSatisfiesRestriction(lprow, lpres, &fSatisfies)) ) { DebugTrace(TEXT("VUE::ScMaybeAddRow() - Error evaluating restriction (SCODE = 0x%08lX)\n"), sc ); return sc; } // If it doesn't, return now. if ( !fSatisfies ) { *pplprow = NULL; return S_OK; } // The row satisfies the restriction, so add it to the row set // according to the specified sort order if ( FAILED(sc = ScAddRow((LPUNKOBJ) lpvue, lpsos, lprow, uliRow, pulcRows, pulcRowMac, pparglprows, pplprow)) ) { DebugTrace(TEXT("VUE::ScMaybeAddRow() - Error adding row (SCODE = 0x%08lX)\n"), sc ); return sc; } return S_OK; } /*============================================================================ - ScAddRow() - * Adds a row to a row set according to the specified sort order returning * a pointer to the location in the row set where the row was added. * * * Parameters: * lpunkobj in UNKOBJ with instance variable containing * allocators. * lpsos in Sort order of row set. * lprow in Row to maybe add. * uliRow in Location to add row if lpsos is NULL * pulcRows in/out Count of rows in the row set. * pparglprows in/out Pointer to buffer containing row set. * pplprow out Pointer to location of added row in row set. */ SCODE ScAddRow( LPUNKOBJ lpunkobj, LPSSortOrderSet lpsos, LPSRow lprow, ULONG uliRow, ULONG * pulcRows, ULONG * pulcRowMac, LPSRow ** pparglprows, LPSRow ** pplprow ) { LPSRow * plprow; SCODE sc = S_OK; Assert(lpsos || uliRow <= *pulcRows); if (HIWORD(*pulcRows + 1) != 0) { sc = MAPI_E_TABLE_TOO_BIG; DebugTrace(TEXT("ScAddRow() - In memory table has > 32767 rows (SCODE = 0x%08lX)\n"), sc ); goto ret; } // Grow the row set if (*pulcRows >= *pulcRowMac) { sc = ScCOReallocate( lpunkobj , (*pulcRowMac + ROW_CHUNK_SIZE) * sizeof(LPSRow) , (LPVOID*) pparglprows); if (FAILED(sc)) { DebugTrace(TEXT("ScAddRow() - Error growing row set (SCODE = 0x%08lX)\n"), sc ); goto ret; } *pulcRowMac += ROW_CHUNK_SIZE; } // Collate the row if ( lpsos ) { plprow = PlprowCollateRow(*pulcRows, *pparglprows, lpsos, TRUE, lprow); } else { plprow = *pparglprows + uliRow; } // And insert it into the row set MoveMemory(plprow+1, plprow, (size_t) (*pulcRows - (plprow - *pparglprows)) * sizeof(LPSRow)); *plprow = lprow; ++*pulcRows; if ( pplprow ) *pplprow = plprow; ret: return sc; } /*============================================================================ * The following functions are generic utility functions to manipulate * table-related data structures. They can easily be modified to be usable * in the common subsystem, and should eventually be put there. The reason * they are here now is to avoid unnecessarily bloating proputil.c until * it can become a lib or DLL. */ /*============================================================================ - ScCopyVueRow() - * For use with IMAPITable::QueryRows(). Copies a row by filling in * the specified SRow with the count of columns in the row and copying, * in order, the specified columns for that row (filling in PT_ERROR * for columns with no value in the row) into a prop value array * allocated using MAPI linked memory. * * * Parameters: * lpunkobj in UNKOBJ with instance variable containing * MAPI allocators. * lpptaCols in Columns to copy. * lprowSrc in Row to copy. * lprowDst out Copied row. */ SCODE ScCopyVueRow( LPVUE lpvue, LPSPropTagArray lpptaCols, LPSRow lprowSrc, LPSRow lprowDst ) { ULONG ulcCols = lpptaCols->cValues; ULONG * pulPropTag; LPSPropValue lppropSrc; LPSPropValue lppropDst; CMB cmb; SCODE sc; ZeroMemory(&cmb, sizeof(CMB)); // Calculate space needed to copy the requested columns pulPropTag = (ULONG *) (lpptaCols->aulPropTag + ulcCols); while ( pulPropTag-- > lpptaCols->aulPropTag ) { lppropSrc = lprowSrc->lpProps + lprowSrc->cValues; while ( lppropSrc-- > lprowSrc->lpProps ) if ( lppropSrc->ulPropTag == *pulPropTag ) { cmb.ulcb += UlcbPropToCopy(lppropSrc); break; } } // Initialize the pointer to NULL in case the allocation fails lprowDst->lpProps = NULL; // Allocate the prop value array for those columns if ( FAILED(sc = ScAllocateBuffer( lpvue, cmb.ulcb + ulcCols * sizeof(SPropValue), &lprowDst->lpProps)) ) { DebugTrace(TEXT("ScCopyRow() - Error allocating row copy (SCODE = 0x%08lX)\n"), sc ); return sc; } MAPISetBufferName(lprowDst->lpProps, TEXT("ITable: one row")); lprowDst->cValues = ulcCols; cmb.lpv = lprowDst->lpProps + ulcCols; // Copy the columns pulPropTag = (ULONG *) (lpptaCols->aulPropTag + ulcCols); lppropDst = lprowDst->lpProps + ulcCols; while ( --pulPropTag, lppropDst-- > lprowDst->lpProps ) { // Find the column in the source row lppropSrc = lprowSrc->lpProps + lprowSrc->cValues; while ( lppropSrc-- > lprowSrc->lpProps ) if ( lppropSrc->ulPropTag == *pulPropTag ) { // Copy it into the dest row SideAssert( PropCopyMore(lppropDst, lppropSrc, (LPALLOCATEMORE) ScBufAllocateMore, &cmb) == S_OK ); goto next_column; } // No corresponding column --> // Copy a null property for this column // // Yeah, we want to do this, but we don't want to do this // on PR_NULL properties! // if (*pulPropTag != PR_NULL) { lppropDst->ulPropTag = PROP_TAG(PT_ERROR,PROP_ID(*pulPropTag)); lppropDst->Value.err = MAPI_E_NOT_FOUND; } else lppropDst->ulPropTag = PR_NULL; next_column: ; } return S_OK; } /*============================================================================ - PlprowByLprow() - * Returns a pointer to where the specified row is in the given row * set. Note! This simply compares pointers to rows for equality. * * * Parameters: * ulcRows in Count of rows in row set. * rglprows in Row set. * lprow in Row to collate. */ LPSRow * PlprowByLprow( ULONG ulcRows, LPSRow * rglprows, LPSRow lprow ) { LPSRow * plprow = NULL; for (plprow = rglprows; ulcRows ; ulcRows--, plprow++ ) { if (*plprow == lprow) return plprow; } return NULL; } /*============================================================================ - PlprowCollateRow() - * Returns a pointer to where the specified row collates in the * specified row set according to the specified sort order set. * A NULL sort order set implies collating at the end of the row set. * * * Parameters: * ulcRows in Count of rows in row set. * rglprows in Row set. * lpsos in Sort order to collate on. * fAfterExisting in True if the row is to go after a range of * equal rows. False means before the range. * lprow in Row to collate. */ LPSRow * PlprowCollateRow( ULONG ulcRows, LPSRow * rglprows, LPSSortOrderSet lpsos, BOOL fAfterExisting, LPSRow lprow ) { LPSRow * plprowMic = rglprows; LPSRow * plprowMac = rglprows + ulcRows; LPSRow * plprow; LPSSortOrder lpso; LPSPropValue lpprop1; LPSPropValue lpprop2; LONG lResult; ULONG i = 0; // If no sort order, collate at the end if ( !lpsos ) return rglprows + ulcRows; // Otherwise, collate the row according to the specified sort order // using a binary search through the rows // Start by checking the last row. This is to speed up the case where // the rows added are already in sort order. plprow = plprowMac - 1; while ( plprowMic < plprowMac ) { lResult = 0; lpso = lpsos->aSort; for (i=0;!lResult && icSorts;i++) { lpprop1 = LpSPropValueFindColumn(lprow, lpso[i].ulPropTag); lpprop2 = LpSPropValueFindColumn(*plprow, lpso[i].ulPropTag); if ( lpprop1 && lpprop2 ) { // If both the row to be collated and the row being // checked against have values for this sort column, // compare the two to determine their relative positions lResult = LPropCompareProp(lpprop1, lpprop2); } else { // Either one or both rows don't have values for this // sort column, so the relative position is determined // by which row (if any) does have a value. lResult = (LONG) (lpprop2 - lpprop1); } // If sorting in reverse order, flip the sense of the comparison if ( lpso[i].ulOrder == TABLE_SORT_DESCEND ) lResult = -lResult; } if ( (lResult > 0) || (!lResult && fAfterExisting) ) { // Row collates after this row plprowMic = plprow + 1; } else { // Row collates before this row plprowMac = plprow; } plprow = (plprowMac - plprowMic) / 2 + plprowMic; } return plprowMic; } /*============================================================================ - UNKOBJ_ScDupRestriction() - * For use with IMAPITable::Restrict(). Makes a copy of the specified * restriction using MAPI linked memory. NULL restrictions are * copied trivially. * * //$BUG ScDupRestriction() calls ScDupRestrictionMore() which is * //$BUG recursive. * * * Parameters: * lpunkobj in UNKOBJ with instance variable containing * MAPI allocators. * lpres in Restriction to copy. * lplpresCopy out Pointer to copied restriction. */ SCODE ScDupRestriction( LPUNKOBJ lpunkobj, LPSRestriction lpres, LPSRestriction FAR * lplpresCopy ) { LPSRestriction lpresCopy; SCODE sc = S_OK; // Duping a NULL restriction is easy.... if ( !lpres ) { *lplpresCopy = NULL; goto ret; } // Allocate space for a more complicated restriction if ( FAILED(sc = ScAllocateBuffer( lpunkobj, sizeof(SRestriction), &lpresCopy)) ) { DebugTrace(TEXT("UNKOBJ::ScDupRestriction() - Error allocating restriction copy (SCODE = 0x%08lX)\n"), sc ); goto ret; } MAPISetBufferName(lpresCopy, TEXT("ITable: copy of restriction")); // And copy it. if ( FAILED(sc = ScDupRestrictionMore( lpunkobj, lpres, lpresCopy, lpresCopy )) ) { DebugTrace(TEXT("UNKOBJ_ScDupRestriction() - Error duping complex restriction (SCODE = 0x%08lX)\n"), sc ); ScFreeBuffer(lpunkobj, lpresCopy); goto ret; } *lplpresCopy = lpresCopy; ret: return sc; } /*============================================================================ - ScDupRestrictionMore() - * For use with IMAPITable::Restrict(). Makes a copy of the specified * restriction using MAPI linked memory. * * //$BUG ScDupRestrictionMore() is recursive. * * * Parameters: * lpunkobj in UNKOBJ with instance variable containing * MAPI allocators. * lpresSrc in Restriction to copy. * lpvLink in Pointer to buffer to which copied restriction * should be linked. * lplpresDst out Pointer to copied restriction. */ SCODE ScDupRestrictionMore( LPUNKOBJ lpunkobj, LPSRestriction lpresSrc, LPVOID lpvLink, LPSRestriction lpresDst ) { SCODE sc = S_OK; switch ( lpresDst->rt = lpresSrc->rt ) { // 'AND' restrictions and 'OR' restrictions have // similar structures, so they can share code // to copy them. // // 'SUB' is about the same as well, only where 'OR' and 'AND' // have a count, it has a subobject. The copy works fine if // you use a count of 1 for the copy, since the "cRes" member // of 'AND' and 'OR' is the same size as the "ulSubObject" // member of 'SUBRESTRICTION'. case RES_AND: case RES_OR: case RES_SUBRESTRICTION: { LONG liRes; lpresDst->res.resAnd.cRes = liRes = lpresSrc->res.resAnd.cRes; if (lpresDst->rt == RES_SUBRESTRICTION) liRes = 1; if ( FAILED(sc = ScAllocateMore( lpunkobj, liRes * sizeof(SRestriction), lpvLink, &lpresDst->res.resAnd.lpRes)) ) { DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'AND' or 'OR' restriction list (SCODE = 0x%08lX)\n"), sc ); goto ret; } while ( --liRes >= 0 ) { if ( FAILED(sc = ScDupRestrictionMore( lpunkobj, lpresSrc->res.resAnd.lpRes + liRes, lpvLink, lpresDst->res.resAnd.lpRes + liRes)) ) { DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'AND' or 'OR' restriction (SCODE = 0x%08lX)\n"), sc ); goto ret; } } goto ret; } case RES_NOT: case RES_COMMENT: { ULONG cValues; // Assert that we can use common code to DUP restriction. Assert( offsetof(SCommentRestriction, lpRes) == offsetof(SNotRestriction, lpRes)); if (FAILED(sc = ScAllocateMore( lpunkobj, sizeof(SRestriction), lpvLink, &lpresDst->res.resComment.lpRes))) { DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc ); goto ret; } if ( FAILED(sc = ScDupRestrictionMore( lpunkobj, lpresSrc->res.resComment.lpRes, lpvLink, lpresDst->res.resComment.lpRes)) ) { DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc ); goto ret; } // Dup the Prop Value array for COMMENT restrictions if (lpresDst->rt == RES_COMMENT) { lpresDst->res.resComment.cValues = lpresSrc->res.resComment.cValues; if ( FAILED(sc = ScAllocateMore( lpunkobj, sizeof(SPropValue)* lpresSrc->res.resComment.cValues, lpvLink, &lpresDst->res.resComment.lpProp)) ) { DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'COMMENT' property (SCODE = 0x%08lX)\n"), sc ); goto ret; } for(cValues=0;cValuesres.resComment.cValues;cValues++) { if ( FAILED(sc = PropCopyMore( lpresDst->res.resComment.lpProp + cValues, lpresSrc->res.resComment.lpProp + cValues, lpunkobj->pinst->lpfAllocateMore, lpvLink )) ) { DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'COMMENT' restriction prop val (SCODE = 0x%08lX)\n"), sc ); goto ret; } } } goto ret; } // 'CONTENT' and 'PROPERTY' restrictions have // similar structures, so they can share code // to copy them case RES_CONTENT: case RES_PROPERTY: { lpresDst->res.resContent.ulFuzzyLevel = lpresSrc->res.resContent.ulFuzzyLevel; lpresDst->res.resContent.ulPropTag = lpresSrc->res.resContent.ulPropTag; if ( FAILED(sc = ScAllocateMore( lpunkobj, sizeof(SPropValue), lpvLink, &lpresDst->res.resContent.lpProp)) ) { DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error allocating 'CONTENT' or 'PROPERTY' property (SCODE = 0x%08lX)\n"), sc ); goto ret; } if ( FAILED(sc = PropCopyMore( lpresDst->res.resContent.lpProp, lpresSrc->res.resContent.lpProp, lpunkobj->pinst->lpfAllocateMore, lpvLink )) ) { DebugTrace(TEXT("UNKOBJ::ScDupRestrictionMore() - Error duping 'CONTENT' or 'PROPERTY' restriction prop val (SCODE = 0x%08lX)\n"), sc ); goto ret; } goto ret; } // Each of these restrictions contain no pointers in their // structures, so they can be copied all the same way // by copying the SRestriction union itself case RES_COMPAREPROPS: case RES_BITMASK: case RES_SIZE: case RES_EXIST: { *lpresDst = *lpresSrc; goto ret; } default: { TrapSz( TEXT("ITABLE:ScDupRestrictionMore - Bad restriction type")); } } ret: return sc; } /*============================================================================ - ScDupRgbEx() - * General utility for copying a hunk of bytes using MAPI allocated * memory. Useful in IMAPITable::SetColumns/QueryColumns to copy * column sets and in IMAPITable::SortTable/QuerySortOrder to copy * sort orders. * * * Parameters: * lpunkobj in UNKOBJ with instance variable containing * MAPI allocators. * ulcb in Count of bytes to copy. * lpb in Buffer to copy from. * lplpbCopy out Pointer to allocated buffer copied to. */ SCODE ScDupRgbEx( LPUNKOBJ lpunkobj, ULONG ulcb, LPBYTE lpb, ULONG ulcbExtra, LPBYTE FAR * lplpbCopy ) { SCODE sc = S_OK; if ( FAILED(sc = ScAllocateBuffer(lpunkobj, ulcb + ulcbExtra, lplpbCopy)) ) { goto ret; } CopyMemory(*lplpbCopy, lpb, (size_t) ulcb); ret: return sc; } /*============================================================================ - ScSatisfiesRestriction() - * Determines if the specified row satisfies the specified restriction * * //$BUG This function is recursive.... * * * Paremeters: * lprow in Row to check. * lpres in Restriction to check against. * pfSatisfies out Pointer to bool which is the result of the * test; TRUE if the row satisfies the * restriction, FALSE otherwise. */ SCODE ScSatisfiesRestriction( LPSRow lprow, LPSRestriction lpres, ULONG * pfSatisfies ) { SCODE sc = S_OK; // Empty restrictions are trivial.... if ( !lpres ) { *pfSatisfies = TRUE; goto ret; } *pfSatisfies = FALSE; switch ( lpres->rt ) { case RES_AND: { ULONG uliRes; for ( uliRes = 0; uliRes < lpres->res.resAnd.cRes; uliRes++ ) { if ( FAILED(sc = ScSatisfiesRestriction( lprow, lpres->res.resAnd.lpRes + uliRes, pfSatisfies)) ) { DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaluating 'AND' restriction (SCODE = 0x%08lX)\n"), sc ); goto ret; } if ( !*pfSatisfies ) break; } goto ret; } case RES_OR: { ULONG uliRes; for ( uliRes = 0; uliRes < lpres->res.resOr.cRes; uliRes++ ) { if ( FAILED(sc = ScSatisfiesRestriction( lprow, lpres->res.resOr.lpRes + uliRes, pfSatisfies)) ) { DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaluating 'OR' restriction (SCODE = 0x%08lX)\n"), sc ); goto ret; } if ( *pfSatisfies ) break; } goto ret; } case RES_COMMENT: case RES_NOT: { // Assert that we can use common code to eval restriction. Assert( offsetof(SCommentRestriction, lpRes) == offsetof(SNotRestriction, lpRes)); if ( FAILED(sc = ScSatisfiesRestriction( lprow, lpres->res.resNot.lpRes, pfSatisfies)) ) { DebugTrace(TEXT("ScSatisfiesRestriction() - Error evaulating 'NOT'or 'COMMENT' restriction (SCODE = 0x%08lX)\n"), sc ); goto ret; } if (lpres->rt == RES_NOT) { *pfSatisfies = !*pfSatisfies; } goto ret; } case RES_CONTENT: { LPSPropValue lpprop; lpprop = LpSPropValueFindColumn(lprow, lpres->res.resContent.ulPropTag); *pfSatisfies = lpprop ? FPropContainsProp( lpprop, lpres->res.resContent.lpProp, lpres->res.resContent.ulFuzzyLevel) : FALSE; goto ret; } case RES_PROPERTY: { LPSPropValue lpprop; *pfSatisfies = FALSE; // Special case for PR_ANR if(lpres->res.resProperty.ulPropTag == PR_ANR_A || lpres->res.resProperty.ulPropTag == PR_ANR_W) { BOOL bUnicode = (PROP_TYPE(lpres->res.resProperty.ulPropTag) == PT_UNICODE); // First check the display name lpprop = LpSPropValueFindColumn(lprow, bUnicode ? PR_DISPLAY_NAME_W : PR_DISPLAY_NAME_A); if(lpprop) { LPTSTR lpT = bUnicode ? lpprop->Value.lpszW : ConvertAtoW(lpprop->Value.lpszA); LPTSTR lpS = bUnicode ? lpres->res.resProperty.lpProp->Value.lpszW : ConvertAtoW(lpres->res.resProperty.lpProp->Value.lpszA); // Do a fuzzy search on this property's string value *pfSatisfies = SubstringSearch( lpT, lpS ); if(!bUnicode) { LocalFreeAndNull(&lpT); LocalFreeAndNull(&lpS); } } if(!*pfSatisfies) { //Display name not found or not matched .. check e-mail address lpprop = LpSPropValueFindColumn(lprow, bUnicode ? PR_EMAIL_ADDRESS_W : PR_EMAIL_ADDRESS_A); if(lpprop) { LPTSTR lpT = bUnicode ? lpprop->Value.lpszW : ConvertAtoW(lpprop->Value.lpszA); LPTSTR lpS = bUnicode ? lpres->res.resProperty.lpProp->Value.lpszW : ConvertAtoW(lpres->res.resProperty.lpProp->Value.lpszA); // Do a fuzzy search on this property's string value *pfSatisfies = SubstringSearch( lpT, lpS ); if(!bUnicode) { LocalFreeAndNull(&lpT); LocalFreeAndNull(&lpS); } } } } else { lpprop = LpSPropValueFindColumn(lprow, lpres->res.resProperty.ulPropTag); if(lpprop) { *pfSatisfies = FPropCompareProp( lpprop, lpres->res.resProperty.relop, lpres->res.resProperty.lpProp); } } goto ret; } case RES_COMPAREPROPS: { LPSPropValue lpprop1; LPSPropValue lpprop2; lpprop1 = LpSPropValueFindColumn(lprow, lpres->res.resCompareProps.ulPropTag1); lpprop2 = LpSPropValueFindColumn(lprow, lpres->res.resCompareProps.ulPropTag2); *pfSatisfies = (lpprop1 && lpprop2) ? FPropCompareProp( lpprop1, lpres->res.resCompareProps.relop, lpprop2) : FALSE; goto ret; } case RES_BITMASK: { LPSPropValue lpprop; lpprop = LpSPropValueFindColumn(lprow, lpres->res.resBitMask.ulPropTag); *pfSatisfies = lpprop ? ((ULONG) !!(lpprop->Value.l & lpres->res.resBitMask.ulMask) == lpres->res.resBitMask.relBMR) : FALSE; goto ret; } case RES_SIZE: { LPSPropValue lpprop; LONG ldcb; *pfSatisfies = FALSE; if ( (lpprop = LpSPropValueFindColumn( lprow, lpres->res.resSize.ulPropTag)) != NULL ) { ldcb = (LONG) lpres->res.resSize.cb - (LONG) UlPropSize(lpprop); switch (lpres->res.resSize.relop) { case RELOP_LT: *pfSatisfies = (0 < ldcb); break; case RELOP_LE: *pfSatisfies = (0 <= ldcb); break; case RELOP_GT: *pfSatisfies = (0 > ldcb); break; case RELOP_GE: *pfSatisfies = (0 >= ldcb); break; case RELOP_EQ: *pfSatisfies = (0 == ldcb); break; case RELOP_NE: *pfSatisfies = (0 != ldcb); break; } } goto ret; } case RES_EXIST: { *pfSatisfies = !!LpSPropValueFindColumn( lprow, lpres->res.resExist.ulPropTag); goto ret; } case RES_SUBRESTRICTION: { sc = MAPI_E_TOO_COMPLEX; goto ret; } default: { TrapSz( TEXT("ITABLE:ScSatisfiesRestriction - Bad restriction type")); } } ret: return sc; } /*============================================================================ - LpSPropValueFindColumn() - * Utility function to find a column in a row given its proptag. * NOTE! This function compares the entire prop tag. PROP_TYPEs MUST * match! * Returns: * a pointer to the column found or, if the row doesn't contain the * specified column, returns NULL. */ LPSPropValue __fastcall LpSPropValueFindColumn( LPSRow lprow, ULONG ulPropTagColumn ) { ULONG i = 0; for (i=0;icValues;i++) { if((lprow->lpProps)[i].ulPropTag == ulPropTagColumn) return (&(lprow->lpProps[i])); } /* if(ulPropTagColumn == PR_ANR) { // This is a special case table restriction if( lpprop->ulPropTag == PR_DISPLAY_NAME || lpprop->ulPropTag == PR_EMAIL_ADDRESS ) return lpprop; } else */ return NULL; } /*============================================================================ - ScBufAllocateMore() - * MAPIAllocateMore-compatible function to use with proputil's * PropCopyMore when copying into an already allocated buffer. * It avoids having PropCopyMore calling MAPIAllocateMore (which * continually allocates memory from the system) resulting * in faster copying at the expense of running through the properties * to copy once first to determine the amount of additional memory * needed to copy them (see UlcbPropToCopy() below). */ STDMETHODIMP_(SCODE) ScBufAllocateMore( ULONG ulcb, LPCMB lpcmb, LPVOID FAR * lplpv ) { ulcb = LcbAlignLcb(ulcb); if ( ulcb > lpcmb->ulcb ) { TrapSz( TEXT("ScBufAllocateMore() - Buffer wasn't big enough for allocations\n") ); return MAPI_E_NOT_ENOUGH_MEMORY; } *((UNALIGNED LPVOID FAR*) lplpv) = lpcmb->lpv; (LPBYTE) lpcmb->lpv += ulcb; lpcmb->ulcb -= ulcb; return S_OK; } /*============================================================================ - UlcbPropToCopy() - * Not to be confused with UlPropSize in proputil! * * UlcbPropToCopy() returns the size, in bytes, needed to store the value * portion of a prop value not including the size of the SPropValue * structure itself plus any necessary alignment padding. * E.g. A PT_I2 property would always have a size equal to * sizeof(SPropValue); a PT_BINARY property would have a size * equal to sizeof(SPropValue) + ALIGN(Value.bin.cb). */ ULONG UlcbPropToCopy( LPSPropValue lpprop ) { ULONG ulcb = 0; LPVOID lpv; UNALIGNED LPWSTR FAR * lplpwstr = NULL; switch ( PROP_TYPE(lpprop->ulPropTag) ) { case PT_I2: case PT_LONG: case PT_R4: case PT_APPTIME: case PT_DOUBLE: case PT_BOOLEAN: case PT_CURRENCY: case PT_SYSTIME: case PT_I8: case PT_ERROR: return 0; case PT_CLSID: return LcbAlignLcb(sizeof(CLSID)); case PT_BINARY: return LcbAlignLcb(lpprop->Value.bin.cb); case PT_STRING8: return LcbAlignLcb((lstrlenA(lpprop->Value.lpszA)+1) * sizeof(CHAR)); #ifndef WIN16 case PT_UNICODE: // ((UNALIGNED LPWSTR *) lpv1) = &(lpprop->Value.lpszW); ulcb = (ULONG) lstrlenW((LPWSTR) lpprop->Value.lpszW); return LcbAlignLcb((ulcb + 1) * sizeof(WCHAR)); #endif // !WIN16 case PT_MV_I2: ulcb = sizeof(short int); break; case PT_MV_LONG: ulcb = sizeof(LONG); break; case PT_MV_R4: ulcb = sizeof(float); break; case PT_MV_APPTIME: case PT_MV_DOUBLE: ulcb = sizeof(double); break; case PT_MV_CURRENCY: ulcb = sizeof(CURRENCY); break; case PT_MV_SYSTIME: ulcb = sizeof(FILETIME); break; case PT_MV_I8: ulcb = sizeof(LARGE_INTEGER); break; case PT_MV_BINARY: { ulcb = lpprop->Value.MVbin.cValues * sizeof(SBinary); lpv = lpprop->Value.MVbin.lpbin + lpprop->Value.MVbin.cValues; while ( ((SBinary FAR *)lpv)-- > lpprop->Value.MVbin.lpbin ) ulcb += LcbAlignLcb(((SBinary FAR *)lpv)->cb); return LcbAlignLcb(ulcb); } case PT_MV_STRING8: { ulcb = sizeof(LPSTR) * lpprop->Value.MVszA.cValues; lpv = lpprop->Value.MVszA.lppszA + lpprop->Value.MVszA.cValues; while ( ((LPSTR FAR *)lpv)-- > lpprop->Value.MVszA.lppszA ) ulcb += (lstrlenA(*(LPSTR FAR *)lpv)+1) * sizeof(CHAR); return LcbAlignLcb(ulcb); } #ifndef WIN16 case PT_MV_UNICODE: { ulcb = sizeof(LPWSTR) * lpprop->Value.MVszW.cValues; // lpv1 = lpprop->Value.MVszW.lppszW; lplpwstr = lpprop->Value.MVszW.lppszW; lplpwstr += lpprop->Value.MVszW.cValues; while (lplpwstr-- > ((UNALIGNED LPWSTR * ) lpprop->Value.MVszW.lppszW) ) ulcb += (lstrlenW(*lplpwstr)+1) * sizeof(WCHAR); return LcbAlignLcb(ulcb); } #endif // !WIN16 } // For multi-valued arrays of constant-size objects... return lpprop->Value.MVi.cValues * LcbAlignLcb(ulcb); } /*============================================================================ - FRowContainsProp() - * Determines whether or not the given row contains and matches the given * property value array. * * Returns TRUE if the row contains and matches all the propery values * * Parameters: * lprow in Row to examine * cValues in number of property values * lpsv in property value array to be compared */ BOOL FRowContainsProp( LPSRow lprow, ULONG cValues, LPSPropValue lpsv) { ULONG uliProp; LPSPropValue lpsvT; Assert(lprow); Assert(lpsv); for (uliProp=0; uliProp < cValues; uliProp++) { // find the column with the same property tag lpsvT=LpSPropValueFindColumn(lprow,lpsv[uliProp].ulPropTag); if (lpsvT==NULL) return FALSE; // do the properties match? if (LPropCompareProp(lpsvT,&lpsv[uliProp])!=0) return FALSE; } return TRUE; } /*============================================================================ - FBookmarkStale() - * If a bookmark is Moving this function determines its uliRow and * returns FALSE. * * If a bookmark has a uliRow that is too big then it is adjusted to * point to the end of the table and marked as Changed. FALSE is returned. * * If a bookmark is marked as Stale or can't be adjusted then it * is remarked as Stale and TRUE is returned * * Parameters: * lpvue in View to check bookmark against * bk in BOOKMARK to check */ BOOL FBookMarkStale( LPVUE lpvue, BOOKMARK bk) { PBK pbk; LPSRow * plprowBk; // bk too big should already have been caught! Assert( bk < cBookmarksMax); pbk = lpvue->rgbk + bk; if (pbk->dwfBKS & dwfBKSStale) { return TRUE; } if ( !(pbk->dwfBKS & dwfBKSMoving) && (pbk->uliRow > lpvue->bkEnd.uliRow)) { pbk->uliRow = lpvue->bkEnd.uliRow; } else if (plprowBk = PlprowByLprow( lpvue->bkEnd.uliRow , lpvue->parglprows , pbk->lprow)) { pbk->uliRow = (ULONG) (plprowBk - lpvue->parglprows); pbk->dwfBKS &= ~dwfBKSMoving; } else if (pbk->dwfBKS & dwfBKSMoving) { TrapSz( TEXT("Moving bookmark lost its row.\n")); pbk->dwfBKS = dwfBKSValid | dwfBKSStale; return TRUE; } return FALSE; } #ifdef WIN16 // Imported INLINE function. /*============================================================================ - FFindColumn() - * Checks a prop tag array to see if a given prop tag exists. * * NOTE! The prop tag must match completely (even type). * * * Parameters: * lpptaCols in Prop tag array to check * ulPropTag in Prop tag to check for. * * Returns: * TRUE if ulPropTag is in lpptaCols * FALSE if ulPropTag is not in lpptaCols */ BOOL FFindColumn( LPSPropTagArray lpptaCols, ULONG ulPropTag ) { ULONG * pulPropTag; pulPropTag = lpptaCols->aulPropTag + lpptaCols->cValues; while ( --pulPropTag >= lpptaCols->aulPropTag ) if ( *pulPropTag == ulPropTag ) return TRUE; return FALSE; } /*============================================================================ - ScFindRow() - * Finds the first row in the table data whose index column property * value is equal to that of the specified property and returns the * location of that row in the table data, or, if no such row exists, * the end of the table data. * * Parameters: * lptad in TAD in which to find row * lpprop in Index property to match * puliRow out Pointer to location of found row * * Error returns: * MAPI_E_INVALID_PARAMETER If proptag of property isn't the TAD's * index column's proptag. * MAPI_E_NOT_FOUND If no matching row is found (*pplprow * is set to lptad->parglprows + * lptad->cRows in this case). */ SCODE ScFindRow( LPTAD lptad, LPSPropValue lpprop, LPSRow * * pplprow) { SCODE sc = S_OK; SRow row; SizedSSortOrderSet(1, sosIndex) = { 1, 0, 0 }; row.ulAdrEntryPad = 0; row.cValues = 1; row.lpProps = lpprop; if (lpprop->ulPropTag != lptad->ulPropTagIndexCol) { sc = MAPI_E_INVALID_PARAMETER; goto ret; } Assert(!IsBadWritePtr(pplprow, sizeof(*pplprow))); // Build a sort order set for the Index Column sosIndex.aSort[0].ulPropTag = lptad->ulPropTagIndexCol; sosIndex.aSort[0].ulOrder = TABLE_SORT_ASCEND; *pplprow = PlprowCollateRow(lptad->ulcRowsIndex, lptad->parglprowIndex, (LPSSortOrderSet) &sosIndex, FALSE, &row); // Find the row in the Index Sorted Row Set if ( !lptad->ulcRowsIndex || (*pplprow >= (lptad->parglprowIndex + lptad->ulcRowsIndex)) || LPropCompareProp( lpprop, (**pplprow)->lpProps)) { sc = MAPI_E_NOT_FOUND; } ret: return sc; } #endif // WIN16 /* - - FixupCols * */ void FixupColsWA(LPSPropTagArray lpptaCols, BOOL bUnicodeTable) { if(!bUnicodeTable) // assumes UNICODE is defined { // We need to mark the table columns as not having UNICODE props ULONG i = 0; for(i = 0;icValues;i++) { switch(PROP_TYPE(lpptaCols->aulPropTag[i])) { case PT_UNICODE: lpptaCols->aulPropTag[i] = CHANGE_PROP_TYPE(lpptaCols->aulPropTag[i], PT_STRING8); break; case PT_MV_UNICODE: lpptaCols->aulPropTag[i] = CHANGE_PROP_TYPE(lpptaCols->aulPropTag[i], PT_MV_STRING8); break; } } } }