/* * ABCONT.C * * Generic IMAPIContainer implementation. * * Copyright 1992 - 1996 Microsoft Corporation. All Rights Reserved. */ #include "_apipch.h" #ifdef WIN16 #undef GetLastError #endif static HRESULT HrGetFirstRowInTad(LPTABLEDATA lpTableData, LPTABLEINFO lpTableInfo, ULONG ulcTableInfo, ULONG uliTable, ULONG * puliRow); static HRESULT HrGetLastRowInTad(LPTABLEDATA lpTableData, LPTABLEINFO lpTableInfo, ULONG ulcTableInfo, ULONG uliTable, ULONG * puliRow); OlkContInfo *FindContainer(LPIAB lpIAB, ULONG cbEntryID, LPENTRYID lpEID); NOTIFCALLBACK lHierarchyNotifCallBack; extern CONTAINER_Vtbl vtblROOT; extern CONTAINER_Vtbl vtblLDAPCONT; extern HRESULT HrSmartResolve(LPIAB lpIAB, LPABCONT lpContainer, ULONG ulFlags, LPADRLIST lpAdrList, LPFlagList lpFlagList, LPAMBIGUOUS_TABLES lpAmbiguousTables); // extern CONTAINER_Vtbl vtblDISTLIST; CONTAINER_Vtbl vtblCONTAINER = { VTABLE_FILL // (CONTAINER_QueryInterface_METHOD *) IAB_QueryInterface, //bug 2707:this crashes CONTAINER_QueryInterface, (CONTAINER_AddRef_METHOD *) WRAP_AddRef, CONTAINER_Release, (CONTAINER_GetLastError_METHOD *) IAB_GetLastError, (CONTAINER_SaveChanges_METHOD *) WRAP_SaveChanges, (CONTAINER_GetProps_METHOD *) WRAP_GetProps, (CONTAINER_GetPropList_METHOD *) WRAP_GetPropList, CONTAINER_OpenProperty, (CONTAINER_SetProps_METHOD *) WRAP_SetProps, (CONTAINER_DeleteProps_METHOD *) WRAP_DeleteProps, (CONTAINER_CopyTo_METHOD *) WRAP_CopyTo, (CONTAINER_CopyProps_METHOD *) WRAP_CopyProps, (CONTAINER_GetNamesFromIDs_METHOD *) WRAP_GetNamesFromIDs, CONTAINER_GetIDsFromNames, CONTAINER_GetContentsTable, CONTAINER_GetHierarchyTable, CONTAINER_OpenEntry, CONTAINER_SetSearchCriteria, CONTAINER_GetSearchCriteria, CONTAINER_CreateEntry, CONTAINER_CopyEntries, CONTAINER_DeleteEntries, CONTAINER_ResolveNames }; // // Interfaces supported by this object // #define CONTAINER_cInterfaces 3 LPIID CONTAINER_LPIID[CONTAINER_cInterfaces] = { (LPIID) &IID_IABContainer, (LPIID) &IID_IMAPIContainer, (LPIID) &IID_IMAPIProp }; #define DISTLIST_cInterfaces 4 LPIID DISTLIST_LPIID[DISTLIST_cInterfaces] = { (LPIID) &IID_IDistList, (LPIID) &IID_IABContainer, (LPIID) &IID_IMAPIContainer, (LPIID) &IID_IMAPIProp }; SizedSSortOrderSet(1, sosPR_ENTRYID) = { 1, 0, 0, { PR_ENTRYID } }; SizedSSortOrderSet(1, sosPR_ROWID) = { 1, 0, 0, { PR_ROWID } }; SizedSPropTagArray(2, tagaInsKey) = { 2, { PR_INSTANCE_KEY, PR_NULL // Space for PR_ROWID } }; // // container default properties // Put essential props first // enum { icdPR_DISPLAY_NAME, icdPR_OBJECT_TYPE, icdPR_CONTAINER_FLAGS, icdPR_DISPLAY_TYPE, icdPR_ENTRYID, // optional icdPR_DEF_CREATE_MAILUSER, // optional icdPR_DEF_CREATE_DL, // optional icdMax }; /*************************************************************************** Name : HrSetCONTAINERAccess Purpose : Sets access flags on a container object Parameters: lpCONTAINER -> Container object ulOpenFlags = MAPI flags: MAPI_MODIFY | MAPI_BEST_ACCESS Returns : HRESULT Comment : Set the access flags on the container. ***************************************************************************/ HRESULT HrSetCONTAINERAccess(LPCONTAINER lpCONTAINER, ULONG ulFlags) { ULONG ulAccess = IPROP_READONLY; switch (ulFlags& (MAPI_MODIFY | MAPI_BEST_ACCESS)) { case MAPI_MODIFY: case MAPI_BEST_ACCESS: ulAccess = IPROP_READWRITE; break; case 0: break; default: Assert(FALSE); } return(lpCONTAINER->lpPropData->lpVtbl->HrSetObjAccess(lpCONTAINER->lpPropData, ulAccess)); } /*************************************************************************** Name : HrNewCONTAINER Purpose : Creates a container object Parameters: lpIAB -> addrbook object ulType = {AB_ROOT, AB_WELL, AB_DL, AB_CONTAINER} lpInterface -> requested interface ulOpenFlags = flags cbEID = size of lpEID lpEID -> optional entryid of this object lpulObjType -> returned object type lppContainer -> returned IABContainer object Returns : HRESULT Comment : ***************************************************************************/ HRESULT HrNewCONTAINER(LPIAB lpIAB, ULONG ulType, LPCIID lpInterface, ULONG ulOpenFlags, ULONG cbEID, LPENTRYID lpEID, ULONG *lpulObjType, LPVOID *lppContainer) { HRESULT hResult = hrSuccess; LPCONTAINER lpCONTAINER = NULL; SCODE sc; LPSPropValue lpProps = NULL; LPPROPDATA lpPropData = NULL; ULONG ulObjectType; BYTE bEntryIDType; ULONG cProps; TCHAR szDisplayName[MAX_PATH] = TEXT(""); LPTSTR lpDisplayName = szDisplayName; BOOL fLoadedLDAP = FALSE; OlkContInfo *polkci; LPPTGDATA lpPTGData=GetThreadStoragePointer(); EnterCriticalSection(&lpIAB->cs); if (lpInterface != NULL) { if (memcmp(lpInterface, &IID_IABContainer, sizeof(IID)) && memcmp(lpInterface, &IID_IDistList, sizeof(IID)) && memcmp(lpInterface, &IID_IMAPIContainer, sizeof(IID)) && memcmp(lpInterface, &IID_IMAPIProp, sizeof(IID))) { hResult = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED); goto exit; } } // // Allocate space for the CONTAINER structure // if ((sc = MAPIAllocateBuffer(sizeof(CONTAINER), (LPVOID *)&lpCONTAINER)) != SUCCESS_SUCCESS) { return(ResultFromScode(sc)); } // [PaulHi] 12/16/98 // We don't set all structure variables so zero out first! ZeroMemory(lpCONTAINER, sizeof(CONTAINER)); lpCONTAINER->pmbinOlk = NULL; switch (ulType) { case AB_ROOT: // Root container object ulObjectType = MAPI_ABCONT; lpCONTAINER->lpVtbl = &vtblROOT; lpCONTAINER->cIID = CONTAINER_cInterfaces; lpCONTAINER->rglpIID = CONTAINER_LPIID; bEntryIDType = WAB_ROOT; #ifdef NEW_STUFF if (! LoadString(hinstMapiX, idsRootName, szDisplayName, ARRAYSIZE(szDisplayName))) { DebugTrace(TEXT("Can't load root name from resource\n")); } #else StrCpyN(szDisplayName, TEXT("WAB Root Container"), ARRAYSIZE(szDisplayName)); #endif MAPISetBufferName(lpCONTAINER, TEXT("AB Root Container Object")); break; case AB_WELL: // What the heck is this supposed to be? Assert(FALSE); hResult = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED); goto exit; break; case AB_DL: // Distribution List container ulObjectType = MAPI_DISTLIST; lpCONTAINER->lpVtbl = &vtblDISTLIST; lpCONTAINER->cIID = DISTLIST_cInterfaces; lpCONTAINER->rglpIID = DISTLIST_LPIID; bEntryIDType = WAB_DISTLIST; MAPISetBufferName(lpCONTAINER, TEXT("AB DISTLIST Container Object")); break; case AB_PAB: // "Default" PAB Container ulObjectType = MAPI_ABCONT; lpCONTAINER->lpVtbl = &vtblCONTAINER; lpCONTAINER->cIID = CONTAINER_cInterfaces; lpCONTAINER->rglpIID = CONTAINER_LPIID; bEntryIDType = WAB_PAB; if (pt_bIsWABOpenExSession) { // if this is an Outlook Session, then the container is the // first one in the list of Outlook containers Assert(lpIAB->lpPropertyStore->rgolkci); lpDisplayName = lpIAB->lpPropertyStore->rgolkci->lpszName; } else if(WAB_PABSHARED == IsWABEntryID(cbEID, lpEID, NULL, NULL, NULL, NULL, NULL)) { // WAB's "shared contacts" container if(FAILED(hResult = MAPIAllocateMore( sizeof(SBinary) + cbEID, lpCONTAINER, (LPVOID *)&lpCONTAINER->pmbinOlk))) goto exit; // The shared contacts container has a special entryid of 0 bytes // and NULL entryid to distinguish it from other entryids lpCONTAINER->pmbinOlk->cb = 0; lpCONTAINER->pmbinOlk->lpb = NULL; LoadString(hinstMapiX, idsSharedContacts, szDisplayName, ARRAYSIZE(szDisplayName)); } else if(bAreWABAPIProfileAware(lpIAB) && bIsThereACurrentUser(lpIAB)) { // if calling client asked for profile support and logging into the // identity manager was successful and returned a valid profile, then // we need to return the user's default folder as the PAB // if(FAILED(hResult = MAPIAllocateMore( sizeof(SBinary) + cbEID, lpCONTAINER, (LPVOID *)&lpCONTAINER->pmbinOlk))) goto exit; lpDisplayName = lpIAB->lpWABCurrentUserFolder->lpFolderName; lpCONTAINER->pmbinOlk->cb = lpIAB->lpWABCurrentUserFolder->sbEID.cb;//cbEID; lpCONTAINER->pmbinOlk->lpb = (LPBYTE)(lpCONTAINER->pmbinOlk + 1); CopyMemory(lpCONTAINER->pmbinOlk->lpb, lpIAB->lpWABCurrentUserFolder->sbEID.lpb, lpCONTAINER->pmbinOlk->cb);//lpEID, cbEID); } else // old style "Contacts" container if (! LoadString(hinstMapiX, idsContacts, szDisplayName, ARRAYSIZE(szDisplayName))) { DebugTrace(TEXT("Can't load pab name from resource\n")); } MAPISetBufferName(lpCONTAINER, TEXT("AB PAB Container Object")); break; case AB_CONTAINER: // regular container/folder - we have an identifying entryid ulObjectType = MAPI_ABCONT; lpCONTAINER->lpVtbl = &vtblCONTAINER; lpCONTAINER->cIID = CONTAINER_cInterfaces; lpCONTAINER->rglpIID = CONTAINER_LPIID; bEntryIDType = WAB_CONTAINER; if (pt_bIsWABOpenExSession || bIsWABSessionProfileAware(lpIAB)) { // If this is an outlook session or if this is an identity-aware // session, look for the specified container and use it polkci = FindContainer(lpIAB, cbEID, lpEID); if (!polkci) { hResult = ResultFromScode(MAPI_E_NOT_FOUND); goto exit; } lpDisplayName = polkci->lpszName; hResult = MAPIAllocateMore( sizeof(SBinary) + cbEID, lpCONTAINER, (LPVOID *)&lpCONTAINER->pmbinOlk); if (FAILED(hResult)) goto exit; lpCONTAINER->pmbinOlk->cb = cbEID; lpCONTAINER->pmbinOlk->lpb = (LPBYTE)(lpCONTAINER->pmbinOlk + 1); CopyMemory(lpCONTAINER->pmbinOlk->lpb, lpEID, cbEID); } MAPISetBufferName(lpCONTAINER, TEXT("AB Container Object")); break; case AB_LDAP_CONTAINER: // LDAP container ulObjectType = MAPI_ABCONT; lpCONTAINER->lpVtbl = &vtblLDAPCONT; lpCONTAINER->cIID = CONTAINER_cInterfaces; lpCONTAINER->rglpIID = CONTAINER_LPIID; bEntryIDType = WAB_LDAP_CONTAINER; // Extract the server name from the LDAP entryid IsWABEntryID(cbEID, lpEID,&lpDisplayName, NULL,NULL, NULL, NULL); fLoadedLDAP = InitLDAPClientLib(); MAPISetBufferName(lpCONTAINER, TEXT("AB LDAP Container Object")); break; default: // shouldnt' hit this one. MAPISetBufferName(lpCONTAINER, TEXT("AB Container Object")); Assert(FALSE); } lpCONTAINER->lcInit = 1; lpCONTAINER->hLastError = hrSuccess; lpCONTAINER->idsLastError = 0; lpCONTAINER->lpszComponent = NULL; lpCONTAINER->ulContext = 0; lpCONTAINER->ulLowLevelError = 0; lpCONTAINER->ulErrorFlags = 0; lpCONTAINER->lpMAPIError = NULL; lpCONTAINER->ulType = ulType; lpCONTAINER->lpIAB = lpIAB; lpCONTAINER->fLoadedLDAP = fLoadedLDAP; // Addref our parent IAB object UlAddRef(lpIAB); // // Create IPropData // if (FAILED(sc = CreateIProp(&IID_IMAPIPropData, (ALLOCATEBUFFER FAR * ) MAPIAllocateBuffer, (ALLOCATEMORE FAR *) MAPIAllocateMore, MAPIFreeBuffer, NULL, &lpPropData))) { hResult = ResultFromScode(sc); goto exit; } MAPISetBufferName(lpPropData, TEXT("lpPropData in HrNewCONTAINER")); if (sc = MAPIAllocateBuffer(icdMax * sizeof(SPropValue), &lpProps)) { hResult = ResultFromScode(sc); } // Set the basic set of properties on this container object such as // display-name etc lpProps[icdPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE; lpProps[icdPR_OBJECT_TYPE].Value.l = ulObjectType; lpProps[icdPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME; lpProps[icdPR_DISPLAY_NAME].Value.LPSZ = lpDisplayName; lpProps[icdPR_CONTAINER_FLAGS].ulPropTag = PR_CONTAINER_FLAGS; lpProps[icdPR_CONTAINER_FLAGS].Value.l = (ulType == AB_ROOT) ? AB_UNMODIFIABLE : AB_MODIFIABLE; lpProps[icdPR_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE; lpProps[icdPR_DISPLAY_TYPE].Value.l = DT_LOCAL; cProps = 4; // in addition to the above properties, add some additional ones depending // on what type of container this is... switch (ulType) { case AB_PAB: lpProps[icdPR_ENTRYID].ulPropTag = PR_ENTRYID; if(lpCONTAINER->pmbinOlk) { // if we have an entryid for the container, just reuse it lpProps[icdPR_ENTRYID].Value.bin.cb = lpCONTAINER->pmbinOlk->cb; //cbEID; lpProps[icdPR_ENTRYID].Value.bin.lpb = lpCONTAINER->pmbinOlk->lpb;//(LPBYTE)lpEID; } else // create a wab entryid that we can hand about if (HR_FAILED(hResult = CreateWABEntryID(bEntryIDType, NULL, NULL, NULL,0, 0, (LPVOID) lpProps, (LPULONG) (&lpProps[icdPR_ENTRYID].Value.bin.cb), (LPENTRYID *)&lpProps[icdPR_ENTRYID].Value.bin.lpb))) { goto exit; } cProps++; // Add the default template IDs used for creating new users lpProps[icdPR_DEF_CREATE_MAILUSER].ulPropTag = PR_DEF_CREATE_MAILUSER; if (HR_FAILED(hResult = CreateWABEntryID(WAB_DEF_MAILUSER, NULL, NULL, NULL, 0, 0, (LPVOID) lpProps, // lpRoot (LPULONG) (&lpProps[icdPR_DEF_CREATE_MAILUSER].Value.bin.cb), (LPENTRYID *)&lpProps[icdPR_DEF_CREATE_MAILUSER].Value.bin.lpb))) { goto exit; } cProps++; lpProps[icdPR_DEF_CREATE_DL].ulPropTag = PR_DEF_CREATE_DL; if (HR_FAILED(hResult = CreateWABEntryID(WAB_DEF_DL, NULL, NULL, NULL, 0, 0, (LPVOID) lpProps, // lpRoot (LPULONG) (&lpProps[icdPR_DEF_CREATE_DL].Value.bin.cb), (LPENTRYID *)&lpProps[icdPR_DEF_CREATE_DL].Value.bin.lpb))) { goto exit; } cProps++; break; case AB_CONTAINER: lpProps[icdPR_ENTRYID].ulPropTag = PR_ENTRYID; lpProps[icdPR_ENTRYID].Value.bin.cb = cbEID; lpProps[icdPR_ENTRYID].Value.bin.lpb = (LPBYTE)lpEID; cProps++; lpProps[icdPR_DEF_CREATE_MAILUSER].ulPropTag = PR_DEF_CREATE_MAILUSER; if (HR_FAILED(hResult = CreateWABEntryID(WAB_DEF_MAILUSER, NULL, NULL, NULL, 0, 0, (LPVOID) lpProps, // lpRoot (LPULONG) (&lpProps[icdPR_DEF_CREATE_MAILUSER].Value.bin.cb), (LPENTRYID *)&lpProps[icdPR_DEF_CREATE_MAILUSER].Value.bin.lpb))) { goto exit; } cProps++; lpProps[icdPR_DEF_CREATE_DL].ulPropTag = PR_DEF_CREATE_DL; if (HR_FAILED(hResult = CreateWABEntryID(WAB_DEF_DL, NULL, NULL, NULL, 0, 0, (LPVOID) lpProps, // lpRoot (LPULONG) (&lpProps[icdPR_DEF_CREATE_DL].Value.bin.cb), (LPENTRYID *)&lpProps[icdPR_DEF_CREATE_DL].Value.bin.lpb))) { goto exit; } cProps++; break; case AB_ROOT: lpProps[icdPR_ENTRYID].ulPropTag = PR_ENTRYID; lpProps[icdPR_ENTRYID].Value.bin.cb = 0; lpProps[icdPR_ENTRYID].Value.bin.lpb = NULL; cProps++; lpProps[icdPR_DEF_CREATE_MAILUSER].ulPropTag = PR_DEF_CREATE_MAILUSER; if (HR_FAILED(hResult = CreateWABEntryID(WAB_DEF_MAILUSER, NULL, NULL, NULL, 0, 0, (LPVOID) lpProps, // lpRoot (LPULONG) (&lpProps[icdPR_DEF_CREATE_MAILUSER].Value.bin.cb), (LPENTRYID *)&lpProps[icdPR_DEF_CREATE_MAILUSER].Value.bin.lpb))) { goto exit; } cProps++; lpProps[icdPR_DEF_CREATE_DL].ulPropTag = PR_DEF_CREATE_DL; if (HR_FAILED(hResult = CreateWABEntryID(WAB_DEF_DL, NULL, NULL, NULL, 0, 0, (LPVOID) lpProps, // lpRoot (LPULONG) (&lpProps[icdPR_DEF_CREATE_DL].Value.bin.cb), (LPENTRYID *)&lpProps[icdPR_DEF_CREATE_DL].Value.bin.lpb))) { goto exit; } cProps++; break; case AB_LDAP_CONTAINER: lpProps[icdPR_ENTRYID].ulPropTag = PR_ENTRYID; lpProps[icdPR_ENTRYID].Value.bin.cb = cbEID; lpProps[icdPR_ENTRYID].Value.bin.lpb = (LPBYTE)lpEID; cProps++; // Hack! Don't need PR_DEF_CREATE_* so use those slots for // PR_WAB_LDAP_SERVER. lpProps[icdPR_DEF_CREATE_MAILUSER].ulPropTag = PR_WAB_LDAP_SERVER; lpProps[icdPR_DEF_CREATE_MAILUSER].Value.LPSZ = lpDisplayName; cProps++; break; } // // Set the default properties // if (HR_FAILED(hResult = lpPropData->lpVtbl->SetProps(lpPropData, cProps, lpProps, NULL))) { LPMAPIERROR lpMAPIError = NULL; lpPropData->lpVtbl->GetLastError(lpPropData, hResult, 0, // Ansi only &lpMAPIError); goto exit; } // default object access is ReadOnly (means the container object can't // be modified but it's data can be modified) lpPropData->lpVtbl->HrSetObjAccess(lpPropData, IPROP_READONLY); lpCONTAINER->lpPropData = lpPropData; // All we want to do is initialize the Root container's critical section InitializeCriticalSection(&lpCONTAINER->cs); *lpulObjType = ulObjectType; *lppContainer = (LPVOID)lpCONTAINER; exit: FreeBufferAndNull(&lpProps); if (HR_FAILED(hResult)) { if (fLoadedLDAP) { DeinitLDAPClientLib(); } FreeBufferAndNull(&lpCONTAINER); UlRelease(lpPropData); } LeaveCriticalSection(&lpIAB->cs); return(hResult); } /*************************************************** * * ABContainer methods */ /* * IUnknown */ /*************************************************************************** Name : CONTAINER::QueryInterface Purpose : Calls the IAB_QueryInterface correctly Parameters: Returns : ***************************************************************************/ STDMETHODIMP CONTAINER_QueryInterface(LPCONTAINER lpContainer, REFIID lpiid, LPVOID * lppNewObj) { // Check to see if it has a jump table if (IsBadReadPtr(lpContainer, sizeof(LPVOID))) { // No jump table found return(ResultFromScode(E_INVALIDARG)); } // Check to see if the jump table has at least sizeof IUnknown if (IsBadReadPtr(lpContainer->lpVtbl, 3*sizeof(LPVOID))) { // Jump table not derived from IUnknown return(ResultFromScode(E_INVALIDARG)); } // Check to see that it's IAB_QueryInterface if (lpContainer->lpVtbl->QueryInterface != CONTAINER_QueryInterface) { // Not my jump table return(ResultFromScode(E_INVALIDARG)); } // default to the IAB QueryInterface method return lpContainer->lpIAB->lpVtbl->QueryInterface(lpContainer->lpIAB, lpiid, lppNewObj); } /*************************************************************************** Name : CONTAINER::Release Purpose : Releases the container object Parameters: lpCONTAINER -> Container object Returns : current reference count Comment : Decrememnt lpInit When lcInit == 0, release the parent objects and free up the lpCONTAINER structure ***************************************************************************/ STDMETHODIMP_(ULONG) CONTAINER_Release(LPCONTAINER lpCONTAINER) { #ifdef PARAMETER_VALIDATION // Check to see if it has a jump table if (IsBadReadPtr(lpCONTAINER, sizeof(LPVOID))) { // No jump table found return(1); } #endif // PARAMETER_VALIDATION EnterCriticalSection(&lpCONTAINER->cs); --lpCONTAINER->lcInit; if (lpCONTAINER->lcInit == 0) { // Remove this object from the objects currently on this session. // Not yet implemented... // Remove the associated lpPropData UlRelease(lpCONTAINER->lpPropData); // Set the Jump table to NULL. This way the client will find out // real fast if it's calling a method on a released object. That is, // the client will crash. Hopefully, this will happen during the // development stage of the client. lpCONTAINER->lpVtbl = NULL; // Free error string if allocated from MAPI memory. FreeBufferAndNull(&(lpCONTAINER->lpMAPIError)); // Release the IAB since we addref'd it in root object creation. UlRelease(lpCONTAINER->lpIAB); if (lpCONTAINER->fLoadedLDAP) { DeinitLDAPClientLib(); } LeaveCriticalSection(&lpCONTAINER->cs); DeleteCriticalSection(&lpCONTAINER->cs); // Need to free the object FreeBufferAndNull(&lpCONTAINER); return(0); } LeaveCriticalSection(&lpCONTAINER->cs); return(lpCONTAINER->lcInit); } /* * IMAPIProp */ /*************************************************************************** Name : CONTAINER::OpenProperty Purpose : Opens an object interface on a particular property Parameters: lpCONTAINER -> Container object ulPropTag = property to open lpiid -> requested interface ulInterfaceOptions = ulFlags = lppUnk -> returned object Returns : HRESULT Comment : ***************************************************************************/ STDMETHODIMP CONTAINER_OpenProperty(LPCONTAINER lpCONTAINER, ULONG ulPropTag, LPCIID lpiid, ULONG ulInterfaceOptions, ULONG ulFlags, LPUNKNOWN * lppUnk) { LPIAB lpIAB; LPSTR lpszMessage = NULL; ULONG ulLowLevelError = 0; HRESULT hr; #ifdef PARAMETER_VALIDATION // Validate parameters // Check to see if it has a jump table if (IsBadReadPtr(lpCONTAINER, sizeof(LPVOID))) { // No jump table found hr = ResultFromScode(MAPI_E_INVALID_PARAMETER); return(hr); } if ((ulInterfaceOptions & ~(MAPI_UNICODE)) || (ulFlags & ~(MAPI_DEFERRED_ERRORS))) { return(hr = ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } if (FBadOpenProperty(lpCONTAINER, ulPropTag, lpiid, ulInterfaceOptions, ulFlags, lppUnk)) { return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } #endif // PARAMETER_VALIDATION #ifdef IABCONTAINER_OPENPROPERTY_SUPPORT // ??Were we supporting this? - vm 3/25/97?? EnterCriticalSection(&lpCONTAINER->cs); lpIAB = lpCONTAINER->lpIAB; // // Check to see if I need a display table // if (ulPropTag == PR_CREATE_TEMPLATES) { // // Looking for the display table // // // Check to see if they're expecting a table interface // if (memcmp(lpiid, &IID_IMAPITable, sizeof(IID))) { hr = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED); goto err; } // Check to see if we already have a table EnterCriticalSection(&lpIAB->cs); // // Get a view from the TAD // hr = lpIAB->lpOOData->lpVtbl->HrGetView( lpIAB->lpOOData, (LPSSortOrderSet)&sosPR_ROWID, NULL, 0, (LPMAPITABLE *)lppUnk); // Leave the critical section after we get our view. LeaveCriticalSection(&lpIAB->cs); #ifdef DEBUG if (hr == hrSuccess) { MAPISetBufferName(*lppUnk, TEXT("OneOff Data VUE1 Object")); } #endif goto err; // Maybe error, maybe not... } else if (ulPropTag == PR_CONTAINER_CONTENTS) { // // Check to see if they're expecting a table interface // if (memcmp(lpiid, &IID_IMAPITable, sizeof(IID))) { hr = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED); goto err; } hr = lpCONTAINER->lpVtbl->GetContentsTable(lpCONTAINER, ulInterfaceOptions, (LPMAPITABLE *)lppUnk); goto err; } else if (ulPropTag == PR_CONTAINER_HIERARCHY) { // // Check to see if they're expecting a table interface // if (memcmp(lpiid, &IID_IMAPITable, sizeof(IID))) { hr = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED); goto err; } hr = lpCONTAINER->lpVtbl->GetHierarchyTable(lpCONTAINER, ulInterfaceOptions, (LPMAPITABLE *)lppUnk); goto err; } // // Don't recognize the property they want opened. // hr = ResultFromScode(MAPI_E_NO_SUPPORT); err: LeaveCriticalSection(&lpCONTAINER->cs); #else // IABCONTAINER_OPENPROPERTY_SUPPORT hr = ResultFromScode(MAPI_E_NO_SUPPORT); #endif // IABCONTAINER_OPENPROPERTY_SUPPORT DebugTraceResult(CONTAINER_OpenProperty, hr); return(hr); } /*************************************************************************** Name : CONTAINER_GetGetIDsFromNames Returns : HRESULT Comment : Just default this to the standard GetIdsFromNames that we use everywhere ***************************************************************************/ STDMETHODIMP CONTAINER_GetIDsFromNames(LPCONTAINER lpRoot, ULONG cPropNames, LPMAPINAMEID * lppPropNames, ULONG ulFlags, LPSPropTagArray * lppPropTags) { return HrGetIDsFromNames(lpRoot->lpIAB, cPropNames, lppPropNames, ulFlags, lppPropTags); } /* - - HrDupeOutlookContentsTable * * Since Outlook is unable to provide a Unicode contents table and we can't fo into the * outlook contents table to modify it's data, we have to recreate the contentstable to * create a WAB version of it .. * This is likely to be a big performance issue .. :-( * */ HRESULT HrDupeOutlookContentsTable(LPMAPITABLE lpOlkTable, LPMAPITABLE * lppTable) { HRESULT hr = S_OK; ULONG ulCount = 0, iRow = 0; DWORD dwIndex = 0; LPSRowSet lpsRow = 0, lpSRowSet = NULL; ULONG ulCurrentRow = (ULONG)-1; ULONG ulNum, ulDen, lRowsSeeked; LPTABLEDATA lpTableData = NULL; SCODE sc = 0; // Create a table object if (FAILED(sc = CreateTable( NULL, // LPCIID (ALLOCATEBUFFER FAR *) MAPIAllocateBuffer, (ALLOCATEMORE FAR *) MAPIAllocateMore, MAPIFreeBuffer, NULL, // lpvReserved, TBLTYPE_DYNAMIC, // ulTableType, PR_ENTRYID, // ulPropTagIndexCol, (LPSPropTagArray)&ITableColumnsRoot, // LPSPropTagArray lpptaCols, &lpTableData))) { DebugTrace(TEXT("CreateTable failed %x\n"), sc); hr = ResultFromScode(sc); goto out; } Assert(lpTableData); ((TAD *)lpTableData)->bMAPIUnicodeTable = TRUE; //this is only called for retreiving unicode tables so the flag is true // How big is the outlook table? if(HR_FAILED(hr = lpOlkTable->lpVtbl->GetRowCount(lpOlkTable, 0, &ulCount))) goto out; DebugTrace( TEXT("Table contains %u rows\n"), ulCount); // Allocate the SRowSet if (FAILED(sc = MAPIAllocateBuffer(sizeof(SRowSet) + ulCount * sizeof(SRow),&lpSRowSet))) { DebugTrace(TEXT("Allocation of SRowSet -> %x\n"), sc); hr = ResultFromScode(sc); goto out; } MAPISetBufferName(lpSRowSet, TEXT("Outlook_ContentsTable_Copy SRowSet")); ZeroMemory( lpSRowSet, (UINT) (sizeof(SRowSet) + ulCount * sizeof(SRow))); lpSRowSet->cRows = ulCount; iRow = 0; // Copy UNICODE versions of all the properties from the Outlook table for (dwIndex = 0; dwIndex < ulCount; dwIndex++) { // Get the next row if(HR_FAILED(hr = lpOlkTable->lpVtbl->QueryRows(lpOlkTable, 1, 0, &lpsRow))) goto out; if (lpsRow) { LPSPropValue lpSPVNew = NULL; Assert(lpsRow->cRows == 1); // should have exactly one row ///****INVESTIGATE if we can reuse this prop array without duplicating***/ if(HR_FAILED(hr = HrDupeOlkPropsAtoWC(lpsRow->aRow[0].cValues, lpsRow->aRow[0].lpProps, &lpSPVNew))) goto out; // Attach the props to the SRowSet lpSRowSet->aRow[iRow].lpProps = lpSPVNew; lpSRowSet->aRow[iRow].cValues = lpsRow->aRow[0].cValues; lpSRowSet->aRow[iRow].ulAdrEntryPad = 0; FreeProws(lpsRow); iRow++; } } // Add all this data we just created to the the Table. if (hr = lpTableData->lpVtbl->HrModifyRows(lpTableData, 0, lpSRowSet)) { DebugTraceResult( TEXT("ROOT_GetContentsTable:HrModifyRows"), hr); goto out; } hr = lpTableData->lpVtbl->HrGetView(lpTableData, NULL, ContentsViewGone, 0, lppTable); out: FreeProws(lpSRowSet); // Cleanup table if failure if (HR_FAILED(hr)) { if (lpTableData) { UlRelease(lpTableData); } } return hr; } /*************************************************************************** Name : CONTAINER::GetContentsTable Purpose : Opens a table of the contents of the container. Parameters: lpCONTAINER -> Container object ulFlags = WAB_PROFILE_CONTENTS - When caller opens the PAB container and want's to get the complete set of contents for the current identity without wanting to enumerate each sub-container seperately - they can specify this flag and we'll return everything corresponding to the current identity all in the same table WAB_CONTENTTABLE_NODATA - Internal only flag .. GetContentsTable normally loads a full contents table and if this is followed by SetColumns, SetColumns also loads a full contents table .. so we basically do the same work twice - to reduce this wasted work, caller can specify not to load data the first time but caller must call SetColumns immediately (or this will probably fault) lppTable -> returned table object Returns : HRESULT Comment : ***************************************************************************/ STDMETHODIMP CONTAINER_GetContentsTable (LPCONTAINER lpCONTAINER, ULONG ulFlags, LPMAPITABLE * lppTable) { HRESULT hResult; LPPTGDATA lpPTGData=GetThreadStoragePointer(); #ifdef PARAMETER_VALIDATION // Check to see if it has a jump table if (IsBadReadPtr(lpCONTAINER, sizeof(LPVOID))) { // No jump table found return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (ulFlags & ~(MAPI_DEFERRED_ERRORS|MAPI_UNICODE|WAB_PROFILE_CONTENTS|WAB_CONTENTTABLE_NODATA)) { DebugTraceArg(CONTAINER_GetContentsTable, TEXT("Unknown flags")); //return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } if (IsBadWritePtr(lppTable, sizeof(LPMAPITABLE))) { DebugTraceArg(CONTAINER_GetContentsTable, TEXT("Invalid Flags")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } #endif // PARAMETER_VALIDATION if(pt_bIsWABOpenExSession) { ULONG ulOlkFlags = ulFlags; // This is a WABOpenEx session using outlooks storage provider if(!lpCONTAINER->lpIAB->lpPropertyStore->hPropertyStore) return MAPI_E_NOT_INITIALIZED; // Since the Outlook store doesn't understand the private flags, these // flags need to be filtered out otherwise the outlook store // provider will fail with E_INVALIDARG or something // if(ulOlkFlags & WAB_PROFILE_CONTENTS) ulOlkFlags &= ~WAB_PROFILE_CONTENTS; if(ulOlkFlags & WAB_CONTENTTABLE_NODATA) ulOlkFlags &= ~WAB_CONTENTTABLE_NODATA; if(ulFlags & MAPI_UNICODE && !pt_bIsUnicodeOutlook) { // This version of Outlook can't handle Unicode so don't tell it to else it'll barf ulOlkFlags &= ~MAPI_UNICODE; } // Outlook provides it's own implementation of GetContentsTable for // efficiencies sake otherwise recreating the table going through the // WAB layer would be just too darned slow ... { LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) lpCONTAINER->lpIAB->lpPropertyStore->hPropertyStore; Assert((lpCONTAINER->ulType == AB_PAB) || (lpCONTAINER->ulType == AB_CONTAINER)); hResult = lpWSP->lpVtbl->GetContentsTable(lpWSP, lpCONTAINER->pmbinOlk, ulOlkFlags, lppTable); DebugPrintTrace((TEXT("WABStorageProvider::GetContentsTable returned:%x\n"),hResult)); if( ulFlags & MAPI_UNICODE && !pt_bIsUnicodeOutlook && *lppTable && !HR_FAILED(hResult)) { // This version of Outlook can't handle Unicode // but caller wants unicode, so now we have to go in and tweak this data // manually .. LPMAPITABLE lpWABTable = NULL; if(!HR_FAILED(hResult = HrDupeOutlookContentsTable(*lppTable, &lpWABTable))) { (*lppTable)->lpVtbl->Release(*lppTable); *lppTable = lpWABTable; } } return hResult; } } // Create a new contents table object hResult = NewContentsTable((LPABCONT)lpCONTAINER, lpCONTAINER->lpIAB, ulFlags, NULL, lppTable); if(!(HR_FAILED(hResult)) && *lppTable && (ulFlags & WAB_PROFILE_CONTENTS) && !(ulFlags & WAB_CONTENTTABLE_NODATA)) { // There is a problem with searching multiple subfolders in that the data does not // come back sorted when it is collated across multiple folders. // We need to sort the table before we return it .. it's somewhat inefficient to do this // sort at this point .. ideally the data should be added to the table sorted... LPSSortOrderSet lpSortCriteria = NULL; SCODE sc = MAPIAllocateBuffer(sizeof(SSortOrderSet)+sizeof(SSortOrder), &lpSortCriteria); if(!sc) { lpSortCriteria->cCategories = lpSortCriteria->cExpanded = 0; lpSortCriteria->cSorts = 1; lpSortCriteria->aSort[0].ulPropTag = PR_DISPLAY_NAME; if(!(((LPTAD)(*lppTable))->bMAPIUnicodeTable)) lpSortCriteria->aSort[0].ulPropTag = CHANGE_PROP_TYPE( lpSortCriteria->aSort[0].ulPropTag, PT_STRING8); lpSortCriteria->aSort[0].ulOrder = TABLE_SORT_ASCEND; hResult = (*lppTable)->lpVtbl->SortTable((*lppTable), lpSortCriteria, 0); FreeBufferAndNull(&lpSortCriteria); } else { hResult = MAPI_E_NOT_ENOUGH_MEMORY; } } return(hResult); } /*************************************************************************** Name : CONTAINER::GetHierarchyTable Purpose : Returns the merge of all the root hierarchy tables Parameters: lpCONTAINER -> Container object ulFlags = lppTable -> returned table object Returns : HRESULT Comment : ***************************************************************************/ STDMETHODIMP CONTAINER_GetHierarchyTable (LPCONTAINER lpCONTAINER, ULONG ulFlags, LPMAPITABLE * lppTable) { LPTSTR lpszMessage = NULL; ULONG ulLowLevelError = 0; HRESULT hr = hrSuccess; #ifdef PARAMETER_VALIDATION // Validate parameters // Check to see if it has a jump table if (IsBadReadPtr(lpCONTAINER, sizeof(LPVOID))) { // No jump table found return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // See if I can set the return variable if (IsBadWritePtr (lppTable, sizeof (LPMAPITABLE))) { return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // Check flags: // The only valid flags are CONVENIENT_DEPTH and MAPI_DEFERRED_ERRORS if (ulFlags & ~(CONVENIENT_DEPTH|MAPI_DEFERRED_ERRORS|MAPI_UNICODE)) { DebugTraceArg(CONTAINER_GetHierarchyTable, TEXT("Invalid Flags")); // return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } #endif EnterCriticalSection(&lpCONTAINER->cs); if (lpCONTAINER->ulType != AB_ROOT) { // // Wrong version of this object. Pretend this object doesn't exist. // hr = ResultFromScode(MAPI_E_NO_SUPPORT); goto out; } // // Get a view from the TAD // hr = lpCONTAINER->lpIAB->lpTableData->lpVtbl->HrGetView( lpCONTAINER->lpIAB->lpTableData, (LPSSortOrderSet) &sosPR_ROWID, NULL, 0, lppTable); if (HR_FAILED(hr)) { DebugTrace(TEXT("IAB_GetHierarchyTable Get Tad View failed\n")); goto out; } #ifdef DEBUG if (hr == hrSuccess) { MAPISetBufferName(*lppTable, TEXT("MergeHier VUE Object")); } #endif // If the convenient depth flag was not specified we restrict on // PR_DEPTH == 1. if (!(ulFlags & CONVENIENT_DEPTH)) { SRestriction restrictDepth; SPropValue spvDepth; spvDepth.ulPropTag = PR_DEPTH; spvDepth.Value.l = 0; restrictDepth.rt = RES_PROPERTY; restrictDepth.res.resProperty.relop = RELOP_EQ; restrictDepth.res.resProperty.ulPropTag = PR_DEPTH; restrictDepth.res.resProperty.lpProp = &spvDepth; if (HR_FAILED(hr = (*lppTable)->lpVtbl->Restrict(*lppTable, &restrictDepth, 0))) { DebugTrace(TEXT("IAB_GetHierarchyTable restriction failed\n")); goto out; } } out: LeaveCriticalSection(&lpCONTAINER->cs); DebugTraceResult(CONTAINER_GetHierarchyTable, hr); return(hr); } /*************************************************************************** Name : HrMergeTableRows Purpose : Creates a merged hierarchy r of all the root level hierarchies from the AB providers installed. Parameters: lptadDst -> TABLEDATA object lpmtSrc -> source hierarchy table ulProviderNum = Returns : HRESULT Comment : NOTE: This may be irrelevant for WAB. ***************************************************************************/ HRESULT HrMergeTableRows(LPTABLEDATA lptadDst, LPMAPITABLE lpmtSrc, ULONG ulProviderNum) { HRESULT hResult = hrSuccess; SCODE sc; ULONG ulRowID = ulProviderNum * ((LONG)IAB_PROVIDER_HIERARCHY_MAX + 1); LPSRowSet lpsRowSet = NULL; LPSRow lprowT; if (hResult = HrQueryAllRows(lpmtSrc, NULL, NULL, NULL, 0, &lpsRowSet)) { DebugTrace(TEXT("HrMergeTableRows() - Could not query provider rows.\n")); goto ret; } if (lpsRowSet->cRows >= IAB_PROVIDER_HIERARCHY_MAX) { DebugTrace(TEXT("HrMergeTableRows() - Provider has too many rows.\n")); hResult = ResultFromScode(MAPI_E_TABLE_TOO_BIG); goto ret; } // Set the ROWID to the end since will be looping in reverse order. ulRowID = ulProviderNum * ((LONG) IAB_PROVIDER_HIERARCHY_MAX + 1) + lpsRowSet->cRows; for (lprowT = lpsRowSet->aRow + lpsRowSet->cRows; --lprowT >= lpsRowSet->aRow;) { ULONG cbInsKey; LPBYTE lpbNewKey = NULL; // Make ulRowID zero based ulRowID--; // // Munge the PR_INSTANCE_KEY // if ((lprowT->lpProps[0].ulPropTag != PR_INSTANCE_KEY) || !(cbInsKey = lprowT->lpProps[0].Value.bin.cb) || ((cbInsKey + sizeof(ULONG)) > UINT_MAX) || IsBadReadPtr(lprowT->lpProps[0].Value.bin.lpb, (UINT) cbInsKey)) { // Can't create our INSTANCE_KEY without a valid provider // INSTANCE_KEY DebugTrace(TEXT("HrMergeTableRows - Provider row has no valid PR_INSTANCE_KEY")); continue; } // Allocate a new buffer for munging the instance key if (FAILED(sc = MAPIAllocateMore(cbInsKey + sizeof(ULONG), lprowT->lpProps, &lpbNewKey))) { hResult = ResultFromScode(sc); DebugTrace(TEXT("HrMergeTableRows() - MAPIAllocMore Failed")); goto ret; } *((LPULONG) lpbNewKey) = ulProviderNum; CopyMemory(lpbNewKey + sizeof(ULONG), lprowT->lpProps[0].Value.bin.lpb, cbInsKey); lprowT->lpProps[0].ulPropTag = PR_INSTANCE_KEY; lprowT->lpProps[0].Value.bin.lpb = lpbNewKey; lprowT->lpProps[0].Value.bin.cb = cbInsKey + sizeof(ULONG); // Add the ROWID so that the original order of the providers is // preserved Assert((PROP_ID(lprowT->lpProps[1].ulPropTag) == PROP_ID(PR_ROWID)) || (PROP_ID(lprowT->lpProps[1].ulPropTag) == PROP_ID(PR_NULL))); lprowT->lpProps[1].ulPropTag = PR_ROWID; lprowT->lpProps[1].Value.l = ulRowID; } // Now put them into the TAD all at once. // Note! We now rely on PR_ROWID to keep the rows in order if (HR_FAILED(hResult = lptadDst->lpVtbl->HrModifyRows(lptadDst, 0, lpsRowSet))) { DebugTrace(TEXT("HrMergeTableRows() - Failed to modify destination TAD.\n")); } ret: // // Free up the row set // FreeProws(lpsRowSet); return(hResult); } /*************************************************************************** Name : CONTAINER::OpenEntry Purpose : Opens an entry Parameters: lpCONTAINER -> Container object cbEntryID = size of entryid lpEntryID -> EntryID to open lpInterface -> requested interface or NULL for default. ulFlags = lpulObjType -> returned object type lppUnk -> returned object Returns : HRESULT Comment : Calls up to IAB's OpenEntry. ***************************************************************************/ STDMETHODIMP CONTAINER_OpenEntry(LPCONTAINER lpCONTAINER, ULONG cbEntryID, LPENTRYID lpEntryID, LPCIID lpInterface, ULONG ulFlags, ULONG * lpulObjType, LPUNKNOWN * lppUnk) { #ifdef PARAMETER_VALIDATION // Validate the object. if (BAD_STANDARD_OBJ(lpCONTAINER, CONTAINER_, OpenEntry, lpVtbl)) { // jump table not large enough to support this method return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // Check the entryid parameter. It needs to be big enough to hold an entryid. // Null entryids are valid /* if (lpEntryID) { if (cbEntryID < offsetof(ENTRYID, ab) || IsBadReadPtr((LPVOID)lpEntryID, (UINT)cbEntryID)) { DebugTraceArg(CONTAINER_OpenEntry, TEXT("lpEntryID fails address check")); return(ResultFromScode(MAPI_E_INVALID_ENTRYID)); } NFAssertSz(FValidEntryIDFlags(lpEntryID->abFlags), TEXT("Undefined bits set in EntryID flags\n")); } */ // Don't check the interface parameter unless the entry is something // MAPI itself handles. The provider should return an error if this // parameter is something that it doesn't understand. // At this point, we just make sure it's readable. if (lpInterface && IsBadReadPtr(lpInterface, sizeof(IID))) { DebugTraceArg(CONTAINER_OpenEntry, TEXT("lpInterface fails address check")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (ulFlags & ~(MAPI_MODIFY | MAPI_DEFERRED_ERRORS | MAPI_BEST_ACCESS)) { DebugTraceArg(CONTAINER_OpenEntry, TEXT("Unknown flags used")); // return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } if (IsBadWritePtr((LPVOID)lpulObjType, sizeof(ULONG))) { DebugTraceArg(CONTAINER_OpenEntry, TEXT("lpulObjType")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (IsBadWritePtr((LPVOID)lppUnk, sizeof(LPUNKNOWN))) { DebugTraceArg(CONTAINER_OpenEntry, TEXT("lppUnk")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } #endif // PARAMETER_VALIDATION // Should just call IAB::OpenEntry()... return(lpCONTAINER->lpIAB->lpVtbl->OpenEntry(lpCONTAINER->lpIAB, cbEntryID, lpEntryID, lpInterface, ulFlags, lpulObjType, lppUnk)); } STDMETHODIMP CONTAINER_SetSearchCriteria(LPCONTAINER lpCONTAINER, LPSRestriction lpRestriction, LPENTRYLIST lpContainerList, ULONG ulSearchFlags) { #ifdef PARAMETER_VALIDATION // Validate the object. if (BAD_STANDARD_OBJ(lpCONTAINER, CONTAINER_, SetSearchCriteria, lpVtbl)) { // jump table not large enough to support this method DebugTraceArg(CONTAINER_SetSearchCriteria, TEXT("Bad object/vtble")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // ensure we can read the restriction if (lpRestriction && IsBadReadPtr(lpRestriction, sizeof(SRestriction))) { DebugTraceArg(CONTAINER_SetSearchCriteria, TEXT("Bad Restriction parameter")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (FBadEntryList(lpContainerList)) { DebugTraceArg(CONTAINER_SetSearchCriteria, TEXT("Bad ContainerList parameter")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (ulSearchFlags & ~(STOP_SEARCH | RESTART_SEARCH | RECURSIVE_SEARCH | SHALLOW_SEARCH | FOREGROUND_SEARCH | BACKGROUND_SEARCH)) { DebugTraceArg(CONTAINER_GetSearchCriteria, TEXT("Unknown flags used")); // return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } #endif // PARAMETER_VALIDATION return(ResultFromScode(MAPI_E_NO_SUPPORT)); } /*************************************************************************** Name : CONTAINER::GetSearchCriteria Purpose : Parameters: lpCONTAINER -> Container object ulFlags = lppRestriction -> Restriction to apply to searches lppContainerList -> lpulSearchState -> returned state Returns : HRESULT Comment : Not implemented in WAB. ***************************************************************************/ STDMETHODIMP CONTAINER_GetSearchCriteria(LPCONTAINER lpCONTAINER, ULONG ulFlags, LPSRestriction FAR * lppRestriction, LPENTRYLIST FAR * lppContainerList, ULONG FAR * lpulSearchState) { #ifdef PARAMETER_VALIDATION // Validate the object. if (BAD_STANDARD_OBJ(lpCONTAINER, CONTAINER_, GetSearchCriteria, lpVtbl)) { // jump table not large enough to support this method DebugTraceArg(CONTAINER_GetSearchCriteria, TEXT("Bad object/vtble")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (ulFlags & ~(MAPI_UNICODE)) { DebugTraceArg(CONTAINER_GetSearchCriteria, TEXT("Unknown Flags")); // return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } // ensure we can write the restriction if (lppRestriction && IsBadWritePtr(lppRestriction, sizeof(LPSRestriction))) { DebugTraceArg(CONTAINER_GetSearchCriteria, TEXT("Bad Restriction write parameter")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // ensure we can read the container list if (lppContainerList && IsBadWritePtr(lppContainerList, sizeof(LPENTRYLIST))) { DebugTraceArg(CONTAINER_GetSearchCriteria, TEXT("Bad ContainerList parameter")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (lpulSearchState && IsBadWritePtr(lpulSearchState, sizeof(ULONG))) { DebugTraceArg(CONTAINER_GetSearchCriteria, TEXT("lpulSearchState fails address check")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } #endif // PARAMETER_VALIDATION return(ResultFromScode(MAPI_E_NO_SUPPORT)); } /*************************************************************************** Name : CONTAINER::CreateEntry Purpose : Creates an entry in the container Parameters: lpCONTAINER -> Container object cbEntryID = size of entryid lpEntryID -> entryID of template [ cbEID and lpEID are the Template Entryids In reality, these are actually flags that just tell us internally what kind of object to create ] ulCreateFlags = lppMAPIPropEntry -> returned MAPIProp object Returns : HRESULT Comment : ***************************************************************************/ STDMETHODIMP CONTAINER_CreateEntry(LPCONTAINER lpCONTAINER, ULONG cbEntryID, LPENTRYID lpEntryID, ULONG ulCreateFlags, LPMAPIPROP FAR * lppMAPIPropEntry) { BYTE bType; #ifdef PARAMETER_VALIDATION // Validate the object. if (BAD_STANDARD_OBJ(lpCONTAINER, CONTAINER_, CreateEntry, lpVtbl)) { // jump table not large enough to support this method DebugTraceArg(CONTAINER_CreateEntry, TEXT("Bad object/Vtbl")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // Check the entryid parameter. It needs to be big enough to hold an entryid. // Null entryid are bad /* if (lpEntryID) { if (cbEntryID < offsetof(ENTRYID, ab) || IsBadReadPtr((LPVOID) lpEntryID, (UINT)cbEntryID)) { DebugTraceArg(CONTAINER_CreateEntry, TEXT("lpEntryID fails address check")); return(ResultFromScode(MAPI_E_INVALID_ENTRYID)); } //NFAssertSz(FValidEntryIDFlags(lpEntryID->abFlags), // TEXT("Undefined bits set in EntryID flags\n")); } else { DebugTraceArg(CONTAINER_CreateEntry, TEXT("lpEntryID NULL")); return(ResultFromScode(MAPI_E_INVALID_ENTRYID)); } */ if (ulCreateFlags & ~(CREATE_CHECK_DUP_STRICT | CREATE_CHECK_DUP_LOOSE | CREATE_REPLACE | CREATE_MERGE)) { DebugTraceArg(CONTAINER_CreateEntry, TEXT("Unknown flags used")); // return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } if (IsBadWritePtr(lppMAPIPropEntry, sizeof(LPMAPIPROP))) { DebugTraceArg(CONTAINER_CreateEntry, TEXT("Bad MAPI Property write parameter")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } #endif // PARAMETER_VALIDATION #ifdef NEVER if (lpCONTAINER->ulType == AB_ROOT) return ResultFromScode(MAPI_E_NO_SUPPORT); #endif // NEVER // What kind of entry are we creating? // Default is MailUser // The passed in entryid is the Tempalte entry ID bType = IsWABEntryID(cbEntryID, lpEntryID, NULL, NULL, NULL, NULL, NULL); if (bType == WAB_DEF_MAILUSER || cbEntryID == 0) { // // Create a new (in memory) entry and return it's mapiprop // return(HrNewMAILUSER(lpCONTAINER->lpIAB, lpCONTAINER->pmbinOlk, MAPI_MAILUSER, ulCreateFlags, lppMAPIPropEntry)); } else if (bType == WAB_DEF_DL) { // // Create a new (in memory) distribution list and return it's mapiprop? return(HrNewMAILUSER(lpCONTAINER->lpIAB, lpCONTAINER->pmbinOlk, MAPI_DISTLIST, ulCreateFlags, lppMAPIPropEntry)); } else { DebugTrace(TEXT("CONTAINER_CreateEntry got unknown template entryID\n")); return(ResultFromScode(MAPI_E_INVALID_ENTRYID)); } } /*************************************************************************** Name : CONTAINER::CopyEntries Purpose : Copies a list of entries into this container. Parameters: lpCONTAINER -> Container object lpEntries -> List of entryid's to copy ulUIParam = HWND lpPropgress -> progress dialog structure ulFlags = Returns : HRESULT Comment : Not implemented in WAB. ***************************************************************************/ STDMETHODIMP CONTAINER_CopyEntries(LPCONTAINER lpCONTAINER, LPENTRYLIST lpEntries, ULONG_PTR ulUIParam, LPMAPIPROGRESS lpProgress, ULONG ulFlags) { #ifdef PARAMETER_VALIDATION if (BAD_STANDARD_OBJ(lpCONTAINER, CONTAINER_, CopyEntries, lpVtbl)) { // jump table not large enough to support this method DebugTraceArg(CONTAINER_CopyEntries, TEXT("Bad object/vtbl")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // ensure we can read the container list if (FBadEntryList(lpEntries)) { DebugTraceArg(CONTAINER_CopyEntries, TEXT("Bad Entrylist parameter")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (ulUIParam && ! IsWindow((HWND)ulUIParam)) { DebugTraceArg(CONTAINER_CopyEntries, TEXT("Invalid window handle")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (lpProgress && IsBadReadPtr(lpProgress, sizeof(IMAPIProgress))) { DebugTraceArg(CONTAINER_CopyEntries, TEXT("Bad MAPI Progress parameter")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (ulFlags & ~(AB_NO_DIALOG | CREATE_CHECK_DUP_LOOSE)) { DebugTraceArg(CONTAINER_CreateEntry, TEXT("Unknown flags used")); // return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } #endif // PARAMETER_VALIDATION return(ResultFromScode(MAPI_E_NO_SUPPORT)); } /*************************************************************************** Name : CONTAINER::DeleteEntries Purpose : Delete entries from this container. Parameters: lpCONTAINER -> Container object lpEntries -> list of entryid's to delete ulFlags = Returns : HRESULT Comment : ***************************************************************************/ STDMETHODIMP CONTAINER_DeleteEntries(LPCONTAINER lpCONTAINER, LPENTRYLIST lpEntries, ULONG ulFlags) { ULONG i; HRESULT hResult = hrSuccess; ULONG cDeleted = 0; ULONG cToDelete; #ifndef DONT_ADDREF_PROPSTORE { SCODE sc; if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpCONTAINER->lpIAB->lpPropertyStore)))) { hResult = ResultFromScode(sc); goto exitNotAddRefed; } } #endif #ifdef PARAMETER_VALIDATION if (BAD_STANDARD_OBJ(lpCONTAINER, CONTAINER_, DeleteEntries, lpVtbl)) { // jump table not large enough to support this method DebugTraceArg(CONTAINER_DeleteEntries, TEXT("Bad object/vtbl")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // ensure we can read the container list if (FBadEntryList(lpEntries)) { DebugTraceArg(CONTAINER_DeleteEntries, TEXT("Bad Entrylist parameter")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } if (ulFlags) { DebugTraceArg(CONTAINER_CreateEntry, TEXT("Unknown flags used")); // return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } #endif // PARAMETER_VALIDATION // List of entryids is in lpEntries. This is a counted array of // entryid SBinary structs. cToDelete = lpEntries->cValues; // Delete each entry for (i = 0; i < cToDelete; i++) { if(0 != IsWABEntryID(lpEntries->lpbin[i].cb, (LPENTRYID) lpEntries->lpbin[i].lpb, NULL, NULL, NULL, NULL, NULL)) { DebugTrace(TEXT("CONTAINER_DeleteEntries got bad entryid of size %u\n"), lpEntries->lpbin[i].cb); continue; } hResult = DeleteCertStuff((LPADRBOOK)lpCONTAINER->lpIAB, (LPENTRYID)lpEntries->lpbin[i].lpb, lpEntries->lpbin[i].cb); hResult = HrSaveHotmailSyncInfoOnDeletion((LPADRBOOK) lpCONTAINER->lpIAB, &(lpEntries->lpbin[i])); if (HR_FAILED(hResult = DeleteRecord(lpCONTAINER->lpIAB->lpPropertyStore->hPropertyStore, &(lpEntries->lpbin[i])))) { DebugTraceResult( TEXT("DeleteEntries: DeleteRecord"), hResult); continue; } cDeleted++; } if (! hResult) { if (cDeleted != cToDelete) { hResult = ResultFromScode(MAPI_W_PARTIAL_COMPLETION); DebugTrace(TEXT("DeleteEntries deleted %u of requested %u\n"), cDeleted, cToDelete); } } #ifndef DONT_ADDREF_PROPSTORE ReleasePropertyStore(lpCONTAINER->lpIAB->lpPropertyStore); exitNotAddRefed: #endif return(hResult); } /*************************************************************************** Name : CONTAINER::ResolveNames Purpose : Resolve names from this container. Parameters: lpCONTAINER -> Container object lptagColSet -> Set of property tags to get from each resolved match. ulFlags = flags (none valid) WAB_IGNORE_PROFILES means that even if this is a profile enabled session, search the whole WAB, not just the current container WAB_RESOLVE_ALL_EMAILS - valid if trying to resolve an e-mail address and we want to search across all e-mail addresses not just the default. Should be used sparingly since it's a labor intensive search MAPI_UNICODE - Adrlist strings are in UNICODE and should return them in Unicode lpAdrList -> [in] set of addresses to resolve, [out] resolved addresses. lpFlagList -> [in/out] resolve flags. Returns : HRESULT Comment : ***************************************************************************/ STDMETHODIMP CONTAINER_ResolveNames(LPCONTAINER lpRoot, LPSPropTagArray lptagaColSet, ULONG ulFlags, LPADRLIST lpAdrList, LPFlagList lpFlagList) { LPADRENTRY lpAdrEntry; ULONG i, j; ULONG ulCount = 1; LPSBinary rgsbEntryIDs = NULL; HRESULT hResult = hrSuccess; LPMAPIPROP lpMailUser = NULL; LPSPropTagArray lpPropTags; LPSPropValue lpPropArray = NULL; LPSPropValue lpPropArrayNew = NULL; ULONG ulObjType, cPropsNew; ULONG cValues; SCODE sc = SUCCESS_SUCCESS; LPTSTR lpsz = NULL; #ifndef DONT_ADDREF_PROPSTORE if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpRoot->lpIAB->lpPropertyStore)))) { hResult = ResultFromScode(sc); goto exitNotAddRefed; } #endif #ifdef PARAMETER_VALIDATION if (BAD_STANDARD_OBJ(lpRoot, CONTAINER_, ResolveNames, lpVtbl)) { // jump table not large enough to support this method DebugTraceArg(CONTAINER_ResolveNames, TEXT("Bad object/vtbl")); return(ResultFromScode(MAPI_E_INVALID_PARAMETER)); } // BUGBUG: Should also check lptagColSet, lpAdrList and lpFlagList! if (ulFlags&(~(WAB_IGNORE_PROFILES|WAB_RESOLVE_ALL_EMAILS|MAPI_UNICODE))) { DebugTraceArg(CONTAINER_ResolveNames, TEXT("Unknown flags used")); // return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS)); } #endif // PARAMETER_VALIDATION // if no set of props to return is specified, return the default set lpPropTags = lptagaColSet ? lptagaColSet : (LPSPropTagArray)&ptaResolveDefaults; if(ulFlags & WAB_RESOLVE_ALL_EMAILS) { hResult = HrSmartResolve(lpRoot->lpIAB, (LPABCONT)lpRoot, WAB_RESOLVE_ALL_EMAILS | (ulFlags & MAPI_UNICODE ? WAB_RESOLVE_UNICODE : 0), lpAdrList, lpFlagList, NULL); // If it's too complex, then just search normally if (MAPI_E_TOO_COMPLEX != hResult) { goto exit; } else { hResult = hrSuccess; } } // search for each name in the lpAdrList for (i = 0; i < lpAdrList->cEntries; i++) { // Make sure we don't resolve an entry which is already resolved. if (lpFlagList->ulFlag[i] == MAPI_RESOLVED) { continue; } lpAdrEntry = &(lpAdrList->aEntries[i]); // Search for this address // BUGBUG: For now, we only resolve perfect matches in the PR_DISPLAY_NAME or PR_EMAIL_ADDRESS // all other properties in ADRLIST are ignored // Look through the ADRENTRY for a PR_DISPLAY_NAME and create an SPropRestriction // to pass down to the property store. for (j = 0; j < lpAdrEntry->cValues; j++) { ULONG ulPropTag = lpAdrEntry->rgPropVals[j].ulPropTag; if(!(ulFlags & MAPI_UNICODE) && PROP_TYPE(ulPropTag)==PT_STRING8) ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_UNICODE); if ( ulPropTag == PR_DISPLAY_NAME || ulPropTag == PR_EMAIL_ADDRESS) { ULONG Flags = AB_FUZZY_FAIL_AMBIGUOUS | AB_FUZZY_FIND_ALL; if(!(ulFlags & WAB_IGNORE_PROFILES)) { // if we didn't ask to surpress profile awareness, // and profile awareness is enabled, restrict this search to // the single folder if(bAreWABAPIProfileAware(lpRoot->lpIAB)) Flags |= AB_FUZZY_FIND_PROFILEFOLDERONLY; } ulCount = 1; // Search the property store Assert(lpRoot->lpIAB->lpPropertyStore->hPropertyStore); if(ulFlags & MAPI_UNICODE) { lpsz = lpAdrEntry->rgPropVals[j].Value.lpszW; } else { LocalFreeAndNull(&lpsz); lpsz = ConvertAtoW(lpAdrEntry->rgPropVals[j].Value.lpszA); } if (HR_FAILED(hResult = HrFindFuzzyRecordMatches(lpRoot->lpIAB->lpPropertyStore->hPropertyStore, lpRoot->pmbinOlk, lpsz, Flags, &ulCount, // IN: number of matches to find, OUT: number found &rgsbEntryIDs))) { if (ResultFromScode(hResult) == MAPI_E_AMBIGUOUS_RECIP) { lpFlagList->ulFlag[i] = MAPI_AMBIGUOUS; continue; } else { DebugTraceResult( TEXT("HrFindFuzzyRecordMatches"), hResult); goto exit; } } if (ulCount) { // Was a match found? Assert(rgsbEntryIDs); if (rgsbEntryIDs) { if (ulCount == 1) { // Open the entry and read the properties you care about. if (HR_FAILED(hResult = lpRoot->lpVtbl->OpenEntry(lpRoot, rgsbEntryIDs[0].cb, // cbEntryID (LPENTRYID)(rgsbEntryIDs[0].lpb), // entryid of first match NULL, // interface 0, // ulFlags &ulObjType, // returned object type (LPUNKNOWN *)&lpMailUser))) { // Failed! Hmmm. DebugTraceResult( TEXT("ResolveNames OpenEntry"), hResult); goto exit; } Assert(lpMailUser); if (HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser, lpPropTags, // lpPropTagArray (ulFlags & MAPI_UNICODE) ? MAPI_UNICODE : 0, &cValues, // how many properties were there? &lpPropArray))) { DebugTraceResult( TEXT("ResolveNames GetProps"), hResult); goto exit; } UlRelease(lpMailUser); lpMailUser = NULL; // Now, construct the new ADRENTRY // (Allocate a new one, free the old one. Assert(lpPropArray); // Merge the new props with the ADRENTRY props if (sc = ScMergePropValues(lpAdrEntry->cValues, lpAdrEntry->rgPropVals, // source1 cValues, lpPropArray, // source2 &cPropsNew, &lpPropArrayNew)) { goto exit; } // [PaulHi] 2/1/99 GetProps now returns the requested tag string // types. So if our client is non-UNICODE make sure we convert any // UNICODE string properties to ANSI. if (!(ulFlags & MAPI_UNICODE)) { if(sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), (LPSPropValue ) lpPropArrayNew, (ULONG) cPropsNew, 0)) goto exit; } // Free the original prop value array FreeBufferAndNull((LPVOID *) (&(lpAdrEntry->rgPropVals))); lpAdrEntry->cValues = cPropsNew; lpAdrEntry->rgPropVals = lpPropArrayNew; FreeBufferAndNull(&lpPropArray); // Mark this entry as found. lpFlagList->ulFlag[i] = MAPI_RESOLVED; } else { DebugTrace(TEXT("ResolveNames found more than 1 match... MAPI_AMBIGUOUS\n")); lpFlagList->ulFlag[i] = MAPI_AMBIGUOUS; } FreeEntryIDs(lpRoot->lpIAB->lpPropertyStore->hPropertyStore, ulCount, rgsbEntryIDs); } } break; } } } exit: #ifndef DONT_ADDREF_PROPSTORE ReleasePropertyStore(lpRoot->lpIAB->lpPropertyStore); exitNotAddRefed: #endif FreeBufferAndNull(&lpPropArray); UlRelease(lpMailUser); if(!(ulFlags & MAPI_UNICODE)) LocalFreeAndNull(&lpsz); return(hResult); } #ifdef NOTIFICATION // save for notifications /*************************************************************************** Name : lTableNotifyCallBack Purpose : Callback function for notifications Parameters: lpvContext -> cNotif = lpNotif -> Returns : Comment : ***************************************************************************/ long STDAPICALLTYPE lTableNotifyCallBack(LPVOID lpvContext, ULONG cNotif, LPNOTIFICATION lpNotif) { LPTABLEINFO lpTableInfo = (LPTABLEINFO)lpvContext; HRESULT hResult; LPSRowSet lpsrowsetProv = NULL; LPIAB lpIAB = lpTableInfo->lpIAB; LPTABLEDATA lpTableData; ULONG ulcTableInfo; LPTABLEINFO pargTableInfo; Assert(lpvContext); Assert(lpNotif); Assert(lpTableInfo->lpTable); Assert(lpTableInfo->lpIAB); Assert(! IsBadWritePtr(lpTableInfo->lpIAB, sizeof(IAB))); // To avoid deadlock we will NOT enter the Address Books critical // section. The Address Book must enter our critical section BEFORE // it modifies anything our callback needs // if the container is null then the tableinfo structure is being // used to keep track of the open one off tables otherwise its // being used to keep track of the open hierarchy tables. if (lpTableInfo->lpContainer == NULL) { // open one off table data lpTableData = lpIAB->lpOOData; ulcTableInfo = lpIAB->ulcOOTableInfo; pargTableInfo = lpIAB->pargOOTableInfo; } else { // open hierarchy table data lpTableData =lpIAB->lpTableData; ulcTableInfo =lpIAB->ulcTableInfo; pargTableInfo =lpIAB->pargTableInfo; // While we here, blow away the SearchPath cache #if defined (WIN32) && !defined (MAC) if (fGlobalCSValid) { EnterCriticalSection(&csMapiSearchPath); } else { DebugTrace(TEXT("lTableNotifyCallback: WAB32.DLL already detached.\n")); } #endif FreeBufferAndNull(&(lpIAB->lpspvSearchPathCache)); lpIAB->lpspvSearchPathCache = NULL; #if defined (WIN32) && !defined (MAC) if (fGlobalCSValid) { LeaveCriticalSection(&csMapiSearchPath); } else { DebugTrace(TEXT("lTableNotifyCallback: WAB32.DLL got detached.\n")); } #endif } switch (lpNotif->info.tab.ulTableEvent) { case TABLE_ROW_ADDED: case TABLE_ROW_DELETED: case TABLE_ROW_MODIFIED: case TABLE_CHANGED: { ULONG uliTable; // table has changed. We need to delete all the rows of // this table in the tad and then add all the rows currently // in that table to the tad. We need to find the start and // end row indexes of the tables data in the tad. // get the index of the given table in the table info array for (uliTable=0; uliTable < ulcTableInfo; uliTable++) { if (pargTableInfo[uliTable].lpTable==lpTableInfo->lpTable) { break; } } Assert(uliTable < ulcTableInfo); // Delete all the rows of the table in the tad by querying // all the rows from the TEXT("restricted") view for this provider // and then calling HrDeleteRows. // We'll add all the new rows back later if (HR_FAILED(hResult = HrQueryAllRows(lpTableInfo->lpmtRestricted, NULL, NULL, NULL, 0, &lpsrowsetProv))) { DebugTrace(TEXT("lTableNotifyCallBack() - Can't query rows from restricted view.\n")); goto ret; } if (lpsrowsetProv->cRows) { // Only call HrDeleteRows if there are rows to delete if (HR_FAILED(hResult = lpTableData->lpVtbl->HrDeleteRows(lpTableData, 0, lpsrowsetProv, NULL))) { DebugTrace(TEXT("lTableNotifyCallBack() - Can't delete rows.\n")); goto ret; } } // Add the contents of the provider table back to the TAD. // Seek to the beginning of the input table if (HR_FAILED(hResult = lpTableInfo->lpTable->lpVtbl->SeekRow(lpTableInfo->lpTable , BOOKMARK_BEGINNING, 0, NULL))) { // table must be empty goto ret; } // Add all rows from the given provider back to the merged table. // NOTE! HrMergeTableRows takes a 1 based provider NUMBER not // a provider index. if (HR_FAILED(hResult = HrMergeTableRows(lpTableData, lpTableInfo->lpTable, uliTable + 1))) { //$BUG Handle per provider errors. DebugTrace(TEXT("lTableNotifyCallBack() - HrMergeTableRows returns (hResult = 0x%08lX)\n"), hResult); } break; } } ret: // free the row set returned from MAPITABLE::QueryRows FreeProws(lpsrowsetProv); return(0); } /*************************************************************************** Name : HrGetBookmarkInTad Purpose : Returns the row number in the tabledata object of the row that corresponds to the row at the bookmark in the given table. Parameters: lpTableData -> lpTable -> Bookmark = puliRow -> Returns : HRESULT Comment : ***************************************************************************/ static HRESULT HrGetBookmarkInTad(LPTABLEDATA lpTableData, LPMAPITABLE lpTable, BOOKMARK Bookmark, ULONG * puliRow) { LPSRowSet lpsRowSet = NULL; LPSRow lpsRow; ULONG uliProp; HRESULT hResult = hrSuccess; Assert(lpTableData); Assert(lpTable); Assert(puliRow); // seek to the bookmark in the given table if (HR_FAILED(hResult=lpTable->lpVtbl->SeekRow( lpTable, Bookmark, 0, NULL))) { goto err; } // get the row if (HR_FAILED(hResult=lpTable->lpVtbl->QueryRows( lpTable, (Bookmark==BOOKMARK_END ? -1 : 1), TBL_NOADVANCE, &lpsRowSet))) { goto err; } // find the entryid in the property value array for (uliProp = 0; uliProp < lpsRowSet->aRow[0].cValues; uliProp++) { if (lpsRowSet->aRow[0].lpProps[uliProp].ulPropTag == PR_ENTRYID) { break; } } Assert(uliProp < lpsRowSet->aRow[0].cValues); // Look for the row in the tad with the same entryid. if (HR_FAILED(hResult=lpTableData->lpVtbl->HrQueryRow( lpTableData, lpsRowSet->aRow[0].lpProps+uliProp, &lpsRow, puliRow))) { // can't find the row in the table data should never happen goto err; } // free the row set returned from QueryRows on the tad FreeBufferAndNull(&lpsRow); err: // free the row set returned from MAPITABLE::QueryRows FreeProws(lpsRowSet); return(hResult); } #endif /* - FindContainer - * Given an entryid, searches in the cached list of containers for * the structure containing the container so that we can get * additional container properties out of the strucutre painlessly * * Returns a pointer to an OlkContInfo structure so don't need to free * the returned value */ OlkContInfo *FindContainer(LPIAB lpIAB, ULONG cbEID, LPENTRYID lpEID) { ULONG iolkci, colkci; BOOL ul=FALSE; OlkContInfo *rgolkci; Assert(lpIAB); Assert(lpIAB->lpPropertyStore); // If the WAB session is Profile Aware, then the WAB's list of containers // is cached on the IAB object if(bIsWABSessionProfileAware(lpIAB)) { colkci = lpIAB->cwabci; rgolkci = lpIAB->rgwabci; } else // it's in the list of the Outlook containers { colkci = lpIAB->lpPropertyStore->colkci; rgolkci = lpIAB->lpPropertyStore->rgolkci; } // if we didn't find any cached info, nothing more to do if(!colkci || !rgolkci) return NULL; for (iolkci = 1; iolkci < colkci; iolkci++) { Assert(rgolkci[iolkci].lpEntryID); if (rgolkci[iolkci].lpEntryID && (cbEID == rgolkci[iolkci].lpEntryID->cb)) { // Look for the match and return that item Assert(rgolkci[iolkci].lpEntryID->lpb); if(cbEID && rgolkci[iolkci].lpEntryID->lpb && (0 == memcmp((LPVOID) lpEID,(LPVOID)rgolkci[iolkci].lpEntryID->lpb, cbEID))) { ul = TRUE; break; } } } return(ul ? &(rgolkci[iolkci]) : NULL); }