You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2323 lines
79 KiB
2323 lines
79 KiB
/*
|
|
* 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);
|
|
}
|