Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6831 lines
223 KiB

/*********************************************************************************
IAdrBook.c
- This file contains the code for implementing the IAdrBook object.
Copyright 1992 - 1996 Microsoft Corporation. All Rights Reserved.
Revision History:
03/01/96 Bruce Kelley Copied MAPI code to WAB
***********************************************************************************/
#include <_apipch.h>
#ifdef WIN16
#undef GetLastError
#endif
extern MAPIUID muidOOP;
extern MAPIUID muidProviderSection;
extern SPropTagArray ptagaABSearchPath;
extern void UninitExtInfo();
extern void UninitContextExtInfo();
extern void UIOLEUninit();
extern void SetOutlookRefreshCountData(DWORD dwOlkRefreshCount,DWORD dwOlkFolderRefreshCount);
extern void GetOutlookRefreshCountData(LPDWORD lpdwOlkRefreshCount,LPDWORD lpdwOlkFolderRefreshCount);
extern void LocalFreeSBinary(LPSBinary lpsb);
// USed for cleaning up the IAB object
void IAB_Neuter (LPIAB lpIAB);
typedef enum {
ENTERED_EMAIL_ADDRESS,
RECEIVED_EMAIL_ADDRESS,
AMBIGUOUS_EMAIL_ADDRESS
} RESOLVE_TYPE;
HRESULT HrResolveOneOffs(LPIAB lpIAB, LPADRLIST lpAdrList, LPFlagList lpFlagList, ULONG ulFlags,
RESOLVE_TYPE ResolveType);
//
// IAdrBook jump table is defined here...
//
IAB_Vtbl vtblIAB = {
VTABLE_FILL
IAB_QueryInterface,
IAB_AddRef,
IAB_Release,
IAB_GetLastError,
(IAB_SaveChanges_METHOD *) WRAP_SaveChanges,
(IAB_GetProps_METHOD *) WRAP_GetProps,
(IAB_GetPropList_METHOD *) WRAP_GetPropList,
(IAB_OpenProperty_METHOD *) WRAP_OpenProperty,
(IAB_SetProps_METHOD *) WRAP_SetProps,
(IAB_DeleteProps_METHOD *) WRAP_DeleteProps,
(IAB_CopyTo_METHOD *) WRAP_CopyTo,
(IAB_CopyProps_METHOD *) WRAP_CopyProps,
(IAB_GetNamesFromIDs_METHOD *) WRAP_GetNamesFromIDs,
IAB_GetIDsFromNames,
IAB_OpenEntry,
IAB_CompareEntryIDs,
IAB_Advise,
IAB_Unadvise,
IAB_CreateOneOff,
IAB_NewEntry,
IAB_ResolveName,
IAB_Address,
IAB_Details,
IAB_RecipOptions,
IAB_QueryDefaultRecipOpt,
IAB_GetPAB,
IAB_SetPAB,
IAB_GetDefaultDir,
IAB_SetDefaultDir,
IAB_GetSearchPath,
IAB_SetSearchPath,
IAB_PrepareRecips
};
//
// Interfaces supported by this object
//
#define IAB_cInterfaces 2
LPIID IAB_LPIID[IAB_cInterfaces] = {
(LPIID) &IID_IAddrBook,
(LPIID) &IID_IMAPIProp
};
#define WM_DOWABNOTIFY WM_USER+102
//***************************************************************************************
//
// Private functions
//
//***************************************************************************************
//
// VerifyWABOpenEx session - Outlook has a bad bug where the first thread which calls WABOpenEx
// passes lpIAB to a second thread .. since the second thread didnt call WABOpenEx, it thinks
// this is a regular WAB session and tries to access the WAB Store and crashes - here we set the
// pt_bIsWABOpenExSession based on the flag set on lpIAB
//
// Right now this is only set for the original IAB_methods - wrapped methods from WRAP_methods
// dont call this function - but hopefully this is enough for now ..
//
void VerifyWABOpenExSession(LPIAB lpIAB)
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
pt_bIsWABOpenExSession = lpIAB->lpPropertyStore->bIsWABOpenExSession;
}
/*
- HrLoadNamedProps
-
* Helper function for loading a bunch of named properties at the same time
*
* uMax - # of props
* nStartIndex - starting index (NOTE this assumes that the indexes for the
* properties are contiguous values since we'll gor through a
* loop from nStartIndex to uMax
* lpGUID - GUID identifying the named props
* lppta - returned prop array
-
*/
HRESULT HrLoadNamedProps(LPIAB lpIAB, ULONG uMax, int nStartIndex,
LPGUID lpGUID, LPSPropTagArray * lppta)
{
HRESULT hr = S_OK;
LPMAPINAMEID * lppConfPropNames;
SCODE sc;
ULONG i = 0;
sc = MAPIAllocateBuffer(sizeof(LPMAPINAMEID) * uMax, (LPVOID *) &lppConfPropNames);
if(sc)
{
hr = ResultFromScode(sc);
goto err;
}
for(i=0;i<uMax;i++)
{
sc = MAPIAllocateMore(sizeof(MAPINAMEID), lppConfPropNames, &(lppConfPropNames[i]));
if(sc)
{
hr = ResultFromScode(sc);
goto err;
}
lppConfPropNames[i]->lpguid = lpGUID;
lppConfPropNames[i]->ulKind = MNID_ID;
lppConfPropNames[i]->Kind.lID = nStartIndex + i;
}
hr = ((LPADRBOOK)lpIAB)->lpVtbl->GetIDsFromNames((LPADRBOOK)lpIAB, uMax, lppConfPropNames,
MAPI_CREATE, lppta);
err:
if(lppConfPropNames)
MAPIFreeBuffer(lppConfPropNames);
return hr;
}
/*
- ReadWABCustomColumnProps
- reads the customized Listview properties from the registry
- Right now there are only 2 customizable props
- Customization settings are saved per identity and so need to be read
- from the identities personal key
*
*/
void ReadWABCustomColumnProps(LPIAB lpIAB)
{
HKEY hKey = NULL;
HKEY hKeyRoot = (lpIAB && lpIAB->hKeyCurrentUser) ?
lpIAB->hKeyCurrentUser : HKEY_CURRENT_USER;
PR_WAB_CUSTOMPROP1 = PR_WAB_CUSTOMPROP2 = 0;
szCustomProp1[0] = TEXT('\0');
szCustomProp2[0] = TEXT('\0');
if(ERROR_SUCCESS == RegOpenKeyEx(hKeyRoot, lpNewWABRegKey, 0, KEY_READ, &hKey))
{
int i = 0;
for(i=0;i<2;i++)
{
LPTSTR szPropTag = (i==0?szPropTag1:szPropTag2);
LPTSTR szPropLabel = (i==0?szCustomProp1:szCustomProp2);
LPULONG lpulProp = (i==0? (&PR_WAB_CUSTOMPROP1):(&PR_WAB_CUSTOMPROP2));
DWORD dwSize = sizeof(DWORD);
DWORD dwType = 0;
DWORD dwValue = 0;
TCHAR szTemp[MAX_PATH];
*szTemp = '\0';
if(ERROR_SUCCESS == RegQueryValueEx(hKey, szPropTag, NULL, &dwType, (LPBYTE) &dwValue, &dwSize))
{
if(dwValue && PROP_TYPE(dwValue) == PT_TSTRING)
{
#ifdef COLSEL_MENU
if( ColSel_PropTagToString(dwValue, szTemp, CharSizeOf( szTemp ) ) )
{
StrCpyN(szPropLabel, szTemp, ARRAYSIZE(szCustomProp1));
lstrcpy( szPropLabel, szTemp );
*lpulProp = dwValue;
}
#endif
}
}
}
}
if(hKey)
RegCloseKey(hKey);
}
/*
- HrLoadPrivateWABProps
-
- WAB uses a bunch of named properties internally .. load them all
- upfront - these will be globals accessible from elsewhere all the time
*
*
*/
HRESULT HrLoadPrivateWABProps(LPIAB lpIAB)
{
ULONG i;
HRESULT hr = E_FAIL;
LPSPropTagArray lpta = NULL;
SCODE sc ;
// Load the set of conferencing named props
//
if(HR_FAILED(hr = HrLoadNamedProps( lpIAB, prWABConfMax, OLK_NAMEDPROPS_START,
(LPGUID) &PS_Conferencing, &lpta)))
goto err;
if(lpta)
{
// Set the property types on the returned props
PR_WAB_CONF_SERVERS = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABConfServers], PT_MV_TSTRING);
PR_WAB_CONF_DEFAULT_INDEX = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABConfDefaultIndex], PT_LONG);
PR_WAB_CONF_BACKUP_INDEX = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABConfBackupIndex], PT_LONG);
PR_WAB_CONF_EMAIL_INDEX = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABConfEmailIndex], PT_LONG);
}
ptaUIDetlsPropsConferencing.cValues = prWABConfMax;
ptaUIDetlsPropsConferencing.aulPropTag[prWABConfServers] = PR_WAB_CONF_SERVERS;
ptaUIDetlsPropsConferencing.aulPropTag[prWABConfDefaultIndex] = PR_WAB_CONF_DEFAULT_INDEX;
ptaUIDetlsPropsConferencing.aulPropTag[prWABConfBackupIndex] = PR_WAB_CONF_BACKUP_INDEX;
ptaUIDetlsPropsConferencing.aulPropTag[prWABConfEmailIndex] = PR_WAB_CONF_EMAIL_INDEX;
if(lpta)
MAPIFreeBuffer(lpta);
// Load the set of WAB's internal named props
//
if(HR_FAILED(hr = HrLoadNamedProps( lpIAB, prWABUserMax, WAB_NAMEDPROPS_START,
(LPGUID) &MPSWab_GUID_V4, &lpta)))
goto err;
if(lpta)
{
// Set the property types on the returned props
PR_WAB_USER_PROFILEID = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABUserProfileID], PT_TSTRING);
PR_WAB_USER_SUBFOLDERS = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABUserSubfolders],PT_MV_BINARY);
PR_WAB_HOTMAIL_CONTACTIDS = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABHotmailContactIDs],PT_MV_TSTRING);
PR_WAB_HOTMAIL_MODTIMES = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABHotmailModTimes],PT_MV_TSTRING);
PR_WAB_HOTMAIL_SERVERIDS = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABHotmailServerIDs],PT_MV_TSTRING);
PR_WAB_DL_ONEOFFS = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABDLOneOffs],PT_MV_BINARY);
PR_WAB_IPPHONE = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABIPPhone],PT_TSTRING);
PR_WAB_FOLDER_PARENT = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABFolderParent],PT_MV_BINARY);
PR_WAB_SHAREDFOLDER = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABSharedFolder],PT_LONG);
PR_WAB_FOLDEROWNER = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABFolderOwner],PT_TSTRING);
}
if(lpta)
MAPIFreeBuffer(lpta);
// Load the set of Yomi named props
//
if(HR_FAILED(hr = HrLoadNamedProps( lpIAB, prWABYomiMax, OLK_YOMIPROPS_START,
(LPGUID) &PS_YomiProps, &lpta)))
goto err;
if(lpta)
{
// Set the property types on the returned props
PR_WAB_YOMI_FIRSTNAME = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABYomiFirst], PT_TSTRING);
PR_WAB_YOMI_LASTNAME = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABYomiLast], PT_TSTRING);
PR_WAB_YOMI_COMPANYNAME = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABYomiCompany], PT_TSTRING);
}
if(lpta)
MAPIFreeBuffer(lpta);
// Load the default mailing address property
//
if(HR_FAILED(hr = HrLoadNamedProps( lpIAB, prWABPostalMax, OLK_POSTALID_START,
(LPGUID) &PS_PostalAddressID, &lpta)))
goto err;
if(lpta)
{
// Set the property types on the returned props
PR_WAB_POSTALID = CHANGE_PROP_TYPE(lpta->aulPropTag[prWABPostalID], PT_LONG);
}
err:
if(lpta)
MAPIFreeBuffer(lpta);
return hr;
}
// The WAB's notification engine is currently a hidden window
// that checks for file changes every NOTIFICATIONTIME milliseconds
// If changes are detected, it fires off a generic notification
//
// <TBD> At some point of time this should be made more granular so apps
// get a message telling them which entry changed rather than a generic
// notification
//
///////////////////////////////////////////////////////////////////////////////
// Way to globally turn off all notifications in this process. Needed because
// of global Outlook MAPI allocator weirdness. See the HrSendMail function in
// uimisc.c
//
// Includes one static variable and two helper functions
// [PaulHi]
///////////////////////////////////////////////////////////////////////////////
static BOOL s_bDisableAllNotifications = FALSE;
void vTurnOffAllNotifications()
{
s_bDisableAllNotifications = TRUE;
}
void vTurnOnAllNotifications()
{
s_bDisableAllNotifications = FALSE;
}
#define NOTIFICATIONTIME 2000 //millisecs
#define NOTIFTIMER 777
/*
- IABNotifWndProc
-
* Window procedure for the hidden window on the iadrbook object
* that has a notification timer and processes timer messages only
* Current timer duration is 3 seconds - so every 3 seconds we check
* for changes and fire notifications accordingly
*
*
*/
LRESULT CALLBACK IABNotifWndProc( HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
LPIAB lpIAB = NULL;
switch(uMsg)
{
case WM_CREATE:
{
LPCREATESTRUCT lpCS = (LPCREATESTRUCT) lParam;
lpIAB = (LPIAB) lpCS->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) lpIAB);
// [PaulHi] 4/27/99 Raid 76520. Don't use the notification
// timer for normal WAB store sessions. We still need this
// for Outlook store sessions, however.
if (pt_bIsWABOpenExSession)
{
lpIAB->ulNotifyTimer = SetTimer(hWnd, NOTIFTIMER, //random number
NOTIFICATIONTIME,
0);
}
}
break;
case WM_DOWABNOTIFY:
lpIAB = (LPIAB)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if ( lpIAB && !IsBadReadPtr(lpIAB, sizeof(LPVOID)) && !s_bDisableAllNotifications )
{
DebugTrace(TEXT("*** *** *** Firing WAB Change Notification *** *** ***\n"));
HrWABNotify(lpIAB);
}
break;
case WM_TIMER:
if(wParam == NOTIFTIMER) // is this the WAB timer ID
{
lpIAB = (LPIAB)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (lpIAB &&
!IsBadReadPtr(lpIAB, sizeof(LPVOID)) &&
lpIAB->lpPropertyStore &&
lpIAB->ulNotifyTimer &&
CheckChangedWAB(lpIAB->lpPropertyStore, lpIAB->hMutexOlk,
&lpIAB->dwOlkRefreshCount, &lpIAB->dwOlkFolderRefreshCount,
&(lpIAB->ftLast)))
{
DebugTrace(TEXT("*** *** *** Firing WAB Change Notification *** *** ***\n"));
HrWABNotify(lpIAB);
}
}
break;
case WM_DESTROY:
lpIAB = (LPIAB)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if(lpIAB && lpIAB->ulNotifyTimer)
KillTimer(hWnd, lpIAB->ulNotifyTimer);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) NULL);
break;
default:
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
return 0;
}
static TCHAR szWABNotifClassName[] = TEXT("WAB Notification Engine");
static TCHAR szWABNotifWinName[] = TEXT("WAB Notification Window");
/*
- CreateIABNotificationTimer
-
* Creates a hidden window on the IAB object. This window has a timer
* which is used to check for changes and fire notifications accordingly
*
*
*/
void CreateIABNotificationTimer(LPIAB lpIAB)
{
HINSTANCE hinst = hinstMapiXWAB;
WNDCLASS wc;
HWND hwnd = NULL;
lpIAB->hWndNotify = FALSE;
lpIAB->ulNotifyTimer = 0;
// Register the window class. Ignore any failures; handle those
// when the window is created.
if (!GetClassInfo(hinst, szWABNotifClassName, &wc))
{
ZeroMemory(&wc, sizeof(WNDCLASS));
wc.style = CS_GLOBALCLASS;
wc.hInstance = hinst;
wc.lpfnWndProc = IABNotifWndProc;
wc.lpszClassName = szWABNotifClassName;
(void)RegisterClass(&wc);
}
// Create the window.
hwnd = CreateWindow( szWABNotifClassName,
szWABNotifWinName,
WS_POPUP, // MAPI bug 6111: pass on Win95 hotkey
0, 0, 0, 0,
NULL, NULL,
hinst,
(LPVOID)lpIAB);
if (!hwnd)
{
DebugTrace(TEXT("HrNewIAB: failure creating notification window (0x%lx)\n"), GetLastError());
return;
}
lpIAB->hWndNotify = hwnd;
return;
}
///////////////////////////////////////////////////////////////////////////////
// IABNotifyThreadProc
//
// Worker thread that waits for a WAB file mod notification from the system
// using FindFirstChangeNotification/FindNextChangeNotification functions.
// When the WAB file store has been modified, this thread will call the WAB
// client notification function.
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI IABNotifyThreadProc(LPVOID lpParam)
{
LPIAB lpIAB = (LPIAB)lpParam;
HANDLE hFCN;
HANDLE ahWaitHandles[2];
DWORD dwWaitRtn;
Assert(lpIAB);
// Set up the FindFirstChangeNotification handle
hFCN = FindFirstChangeNotification(
lpIAB->lpwszWABFilePath, // Directory path to watch
FALSE, // bWatchSubtree
FILE_NOTIFY_CHANGE_LAST_WRITE); // Condition(s) to watch
if (INVALID_HANDLE_VALUE == hFCN || NULL == hFCN)
{
Assert(0);
lpIAB->hThreadNotify = INVALID_HANDLE_VALUE;
return 0;
}
ahWaitHandles[0] = hFCN;
ahWaitHandles[1] = lpIAB->hEventKillNotifyThread;
// Wait for file change
while (1)
{
// Wait on file change nofication, or terminate thread event
dwWaitRtn = WaitForMultipleObjects(2, ahWaitHandles, FALSE, INFINITE);
switch (dwWaitRtn)
{
case WAIT_OBJECT_0:
// Reset the file change
if (!FindNextChangeNotification(hFCN))
{
Assert(0);
}
// Distribute store change notifications. Do this on the main
// IAB thread.
if (lpIAB->hWndNotify)
SendMessage(lpIAB->hWndNotify, WM_DOWABNOTIFY, 0, 0);
break;
case WAIT_FAILED:
// If the wait failed then terminate the thread.
Assert(0);
case WAIT_OBJECT_0+1:
// Close the file change notification handle and
// terminate thread.
FindCloseChangeNotification(hFCN);
return 0;
} // end switch
} // end while
}
///////////////////////////////////////////////////////////////////////////////
// CreateIABNotificationThread
//
// Creates a worker thread that waits for a WAB store file modification
///////////////////////////////////////////////////////////////////////////////
void CreateIABNotificationThread(LPIAB lpIAB)
{
DWORD dwThreadID = 0;
LPWSTR lpwszTemp;
// Put together the WAB store file directory path.
LPMPSWab_FILE_INFO lpMPSWabFileInfo = (LPMPSWab_FILE_INFO)(lpIAB->lpPropertyStore->hPropertyStore);
int nLen = lstrlen(lpMPSWabFileInfo->lpszMPSWabFileName);
lpIAB->lpwszWABFilePath = LocalAlloc( LMEM_ZEROINIT, (sizeof(WCHAR) * (nLen+1)) );
if (!lpIAB->lpwszWABFilePath)
{
Assert(0);
return;
}
StrCpyN(lpIAB->lpwszWABFilePath, lpMPSWabFileInfo->lpszMPSWabFileName, nLen+1);
// Remove file name at the end. This will take care of:
// "c:\path\filename", "c:filename", "\\path\filename"
lpwszTemp = lpIAB->lpwszWABFilePath + nLen;
while ( (lpwszTemp != lpIAB->lpwszWABFilePath) &&
(*lpwszTemp != '\\') && (*lpwszTemp != ':') )
{
--lpwszTemp;
}
if (*lpwszTemp == ':')
++lpwszTemp; // Keep ':' but not the '\\' ... Win95 won't accept the latter.
(*lpwszTemp) = '\0';
// Create the kill thread event
lpIAB->hEventKillNotifyThread = CreateEvent(NULL, TRUE, FALSE, NULL);
if (lpIAB->hEventKillNotifyThread == NULL)
{
Assert(0);
return;
}
lpIAB->hThreadNotify = CreateThread(
NULL, // no security attributes
0, // use default stack size
IABNotifyThreadProc, // thread function
(LPVOID)lpIAB, // argument to thread function
0, // use default creation flags
&dwThreadID); // returns the thread identifier
Assert(INVALID_HANDLE_VALUE != lpIAB->hThreadNotify);
}
/*
- HrNewIAB
-
* Creates a new IAddrBook object (also known fondly as IAB object)
*
lpPropertyStore - Handle to the property store
lpWABOBject - the WABOBject for this session (the 2 are closely linked)
lppIAB - returned IAB object
*
*/
HRESULT HrNewIAB(LPPROPERTY_STORE lpPropertyStore,
LPWABOBJECT lpWABObject, LPVOID *lppIAB)
{
LPIAB lpIAB = NULL;
SCODE sc;
HRESULT hr = hrSuccess;
LPSTR lpszMessage = NULL;
ULONG ulLowLevelError = 0;
LPSTR lpszComponent = NULL;
ULONG ulContext = 0;
UINT ids = 0;
ULONG ulMemFlag = 0;
SPropValue spv[1];
LPPROPDATA lpPropData = NULL;
LPMAPIERROR lpMAPIError = NULL;
LPSPropValue lpspvSearchPath = NULL;
BOOL bAddRefedPropStore = FALSE;
BOOL bAddRefedWABObject = FALSE;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
//
// Allocate space for the IAB structure
//
if (FAILED(sc = MAPIAllocateBuffer(sizeof(IAB), (LPVOID *) &lpIAB))) {
hr = ResultFromScode(sc);
ulContext = CONT_SESS_OPENAB_1;
// ids = IDS_NOT_ENOUGH_MEMORY;
goto err;
}
MAPISetBufferName(lpIAB, TEXT("AB Object"));
ZeroMemory(lpIAB, sizeof(IAB));
lpIAB->lpVtbl = &vtblIAB;
lpIAB->cIID = IAB_cInterfaces;
lpIAB->rglpIID = IAB_LPIID;
lpIAB->hThreadNotify = INVALID_HANDLE_VALUE;
// The session's reference to the address book doesn't count. Only
// client references (via SESSOBJ_OpenAddressBook) cause an increase
// in the refcount.
lpIAB->lcInit = 1; // Caller gets an instance
lpIAB->hLastError = hrSuccess;
lpIAB->idsLastError = 0;
lpIAB->lpszComponent = NULL;
lpIAB->ulContext = 0;
lpIAB->ulLowLevelError = 0;
lpIAB->ulErrorFlags = 0;
lpIAB->lpMAPIError = NULL;
lpIAB->lRowID = -1;
if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpPropertyStore)))) {
hr = ResultFromScode(sc);
goto err;
}
bAddRefedPropStore = TRUE;
lpIAB->lpPropertyStore = lpPropertyStore;
lpIAB->lpEntryIDDD = NULL;
lpIAB->cbEntryIDDD = 0;
lpIAB->lpEntryIDPAB = NULL;
lpIAB->cbEntryIDPAB = 0;
lpIAB->lpspvSearchPathCache = NULL;
lpIAB->lpTableData = NULL;
lpIAB->lpOOData = NULL;
lpIAB->ulcTableInfo = 0;
lpIAB->pargTableInfo = NULL;
lpIAB->ulcOOTableInfo = 0;
lpIAB->pargOOTableInfo = NULL;
lpIAB->padviselistIAB = NULL;
lpIAB->pWABAdviseList = NULL;
lpIAB->nPropExtDLLs = 0;
lpIAB->lpPropExtDllList = NULL;
lpIAB->lpWABObject = (LPIWOINT)lpWABObject;
UlAddRef(lpWABObject);
bAddRefedWABObject = TRUE;
// If this session was opened with given Outlook allocator function pointers
// then set the boolean
lpIAB->bSetOLKAllocators = lpIAB->lpWABObject->bSetOLKAllocators;
//
// Create IPropData
//
sc = CreateIProp((LPIID)&IID_IMAPIPropData,
(ALLOCATEBUFFER FAR *) MAPIAllocateBuffer,
(ALLOCATEMORE FAR *) MAPIAllocateMore,
MAPIFreeBuffer,
NULL,
&lpPropData);
if (FAILED(sc)) {
hr = ResultFromScode(sc);
ulContext = CONT_SESS_OPENAB_2;
// ids = IDS_NOT_ENOUGH_MEMORY;
goto err;
}
MAPISetBufferName(lpPropData, TEXT("lpPropData in HrNewIAB"));
// PR_OBJECT_TYPE
spv[0].ulPropTag = PR_OBJECT_TYPE;
spv[0].Value.l = MAPI_ADDRBOOK;
//
// Set the default properties
//
if (HR_FAILED(hr = lpPropData->lpVtbl->SetProps(lpPropData,
1,
spv,
NULL))) {
lpPropData->lpVtbl->GetLastError(lpPropData,
hr,
0,
&lpMAPIError);
ids = 0;
ulMemFlag = 1;
goto err;
}
// object itself can't be modified
lpPropData->lpVtbl->HrSetObjAccess(lpPropData, IPROP_READONLY);
lpIAB->lpPropData = lpPropData;
lpIAB->fLoadedLDAP = FALSE;
//if (ResolveLDAPServers()) {
// // load the LDAP client dll
// lpIAB->fLoadedLDAP = InitLDAPClientLib();
//}
// Create a notification timer for this IAddrBook obect
// for handline IAddrBook::Advise calls.
// [PaulHi] 4/27/99 This just creates the notification "hidden" window. The
// timer will be created only if this is an Outlook session. Otherwise the
// FCN wait thread uses this to notify IAB clients of a store change ... using
// the original thread the IAB was created on.
CreateIABNotificationTimer(lpIAB);
// [PaulHi] 4/27/99 Raid 76520 Use FCN wait thread instead
// of notify window timer for non-Outlook sessions.
if (!pt_bIsWABOpenExSession)
CreateIABNotificationThread(lpIAB);
// All we want to do is initialize the IABs critical section
// We are already in a SessObj critical section.
InitializeCriticalSection(&lpIAB->cs);
lpIAB->hMutexOlk = CreateMutex(NULL, FALSE, TEXT("MPSWABOlkStoreNotifyMutex"));
if(GetLastError()!=ERROR_ALREADY_EXISTS)
{
// First one to create the mutex ... means we can reset the reg settings
SetOutlookRefreshCountData(0,0);
}
GetOutlookRefreshCountData(&lpIAB->dwOlkRefreshCount,&lpIAB->dwOlkFolderRefreshCount);
*lppIAB = (LPVOID)lpIAB;
return(hrSuccess);
err:
FreeBufferAndNull(&lpIAB);
UlRelease(lpPropData);
if(bAddRefedWABObject)
UlRelease(lpWABObject);
if(bAddRefedPropStore)
ReleasePropertyStore(lpPropertyStore); // undo the above operation
return(hr);
}
/*
- SetMAPIError
-
*
* Parameters:
* lpObject
* hr
* ids - ID of string resource associated with an internal error string
* lpszComponent - Constant string, not allocated (must be ANSI)
* ulContext
* ulLowLevelError
* ulErrorFlags - Whether or not the lpMAPIError is UNICODE or not (MAPI_UNICODE)
* lpMAPIError - Allocated, generally from foreign object.
*/
VOID SetMAPIError(LPVOID lpObject,
HRESULT hr,
UINT ids,
LPTSTR lpszComponent,
ULONG ulContext,
ULONG ulLowLevelError,
ULONG ulErrorFlags,
LPMAPIERROR lpMAPIError)
{
LPIAB lpIAB = (LPIAB) lpObject;
lpIAB->hLastError = hr;
lpIAB->ulLowLevelError = ulLowLevelError;
lpIAB->ulContext = ulContext;
// Free any existing MAPI error
FreeBufferAndNull(&(lpIAB->lpMAPIError));
// If both a MAPIERROR and a string ID are present then we will
// concatenate them when the error is reported.
lpIAB->lpMAPIError = lpMAPIError;
lpIAB->ulErrorFlags = ulErrorFlags;
lpIAB->idsLastError = ids;
lpIAB->lpszComponent = lpszComponent;
return;
}
/***************************************************
*
* The actual IAdrBook methods
*/
// --------
// IUnknown
STDMETHODIMP
IAB_QueryInterface(LPIAB lpIAB,
REFIID lpiid,
LPVOID * lppNewObj)
{
ULONG iIID;
#ifdef PARAMETER_VALIDATION
// Check to see if it has a jump table
if (IsBadReadPtr(lpIAB, sizeof(LPVOID))) {
// No jump table found
return(ResultFromScode(E_INVALIDARG));
}
// Check to see if the jump table has at least sizeof IUnknown
if (IsBadReadPtr(lpIAB->lpVtbl, 3*sizeof(LPVOID))) {
// Jump table not derived from IUnknown
return(ResultFromScode(E_INVALIDARG));
}
// Check to see that it's IAB_QueryInterface
if (lpIAB->lpVtbl->QueryInterface != IAB_QueryInterface) {
// Not my jump table
return(ResultFromScode(E_INVALIDARG));
}
// Is there enough there for an interface ID?
if (IsBadReadPtr(lpiid, sizeof(IID))) {
DebugTraceSc(IAB_QueryInterface, E_INVALIDARG);
return(ResultFromScode(E_INVALIDARG));
}
// Is there enough there for a new object?
if (IsBadWritePtr (lppNewObj, sizeof (LPIAB))) {
DebugTraceSc(IAB_QueryInterface, E_INVALIDARG);
return(ResultFromScode(E_INVALIDARG));
}
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpIAB->cs);
// See if the requested interface is one of ours
//
// First check with IUnknown, since we all have to support that one...
//
if (! memcmp(lpiid, &IID_IUnknown, sizeof(IID))) {
goto goodiid;
}
//
// Now look through all the iids associated with this object, see if any match
//
for(iIID = 0; iIID < lpIAB->cIID; iIID++) {
if (!memcmp(lpIAB->rglpIID[iIID], lpiid, sizeof(IID))) {
goodiid:
//
// It's a match of interfaces, we support this one then...
//
++lpIAB->lcInit;
// Bug 48468 - we're not addrefing the WABObject here but
// we're releasing it in the IadrBook->Release
//
UlAddRef(lpIAB->lpWABObject);
*lppNewObj = lpIAB;
LeaveCriticalSection(&lpIAB->cs);
return(0);
}
}
//
// No interface we've heard of...
//
LeaveCriticalSection(&lpIAB->cs);
*lppNewObj = NULL; // OLE requires NULLing out parm on failure
DebugTraceSc(IAB_QueryInterface, E_NOINTERFACE);
return(ResultFromScode(E_NOINTERFACE));
}
/**************************************************
*
* IAB_AddRef
* Increment lcInit
*
*/
STDMETHODIMP_(ULONG) IAB_AddRef (LPIAB lpIAB)
{
#ifdef PARAMETER_VALIDATION
// Check to see if it has a jump table
if (IsBadReadPtr(lpIAB, sizeof(LPVOID))) {
//No jump table found
return(1);
}
// Check to see if the jump table has at least sizeof IUnknown
if (IsBadReadPtr(lpIAB->lpVtbl, 3 * sizeof(LPVOID))) {
// Jump table not derived from IUnknown
return(1);
}
// Check to see if the method is the same
if ((IAB_AddRef != lpIAB->lpVtbl->AddRef)
#ifdef DEBUG
// For spooler session leak tracking
//&& ((IAB_AddRef_METHOD *)SESSOBJ_AddRef != lpIAB->lpVtbl->AddRef)
#endif
) {
// Wrong object - the object passed doesn't have this
// method.
return(1);
}
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpIAB->cs);
++lpIAB->lcInit;
LeaveCriticalSection(&lpIAB->cs);
UlAddRef(lpIAB->lpWABObject);
return(lpIAB->lcInit);
}
/**************************************************
*
* IAB_Release
* Decrement lpInit.
* When lcInit == 0, free up the lpIAB structure
*
*/
STDMETHODIMP_(ULONG)
IAB_Release (LPIAB lpIAB)
{
UINT uiABRef;
BOOL bSetOLKAllocators;
#ifdef PARAMETER_VALIDATION
// Check to see if it has a jump table
if (IsBadReadPtr(lpIAB, sizeof(LPVOID))) {
// No jump table found
return(1);
}
// Check to see if the jump table has at least sizeof IUnknown
if (IsBadReadPtr(lpIAB->lpVtbl, 3*sizeof(LPVOID))) {
// Jump table not derived from IUnknown
return(1);
}
// Check to see if the method is the same
if (IAB_Release != lpIAB->lpVtbl->Release) {
// Wrong object - the object passed doesn't have this
// method.
return(1);
}
#endif // PARAMETER_VALIDATION
VerifyWABOpenExSession(lpIAB);
// The address book is part of the session object, and one doesn't go away
// without the other going away also. This code checks that both the
// session and the address book have a zero refcount before bringing
// them both down. Note that when I want to get both critical sections,
// I get the session object CS first. This avoids deadlock.
// See SESSOBJ_Release in isess.c for very similar code.
UlRelease(lpIAB->lpWABObject);
EnterCriticalSection(&lpIAB->cs);
if (lpIAB->lcInit == 0) {
uiABRef = 0;
} else {
AssertSz(lpIAB->lcInit < UINT_MAX, TEXT("Overflow in IAB Reference count"));
uiABRef = (UINT) --(lpIAB->lcInit);
}
LeaveCriticalSection(&lpIAB->cs);
if (uiABRef) {
return((ULONG)uiABRef);
}
EnterCriticalSection(&lpIAB->cs);
uiABRef = (UINT) lpIAB->lcInit;
LeaveCriticalSection(&lpIAB->cs);
if (uiABRef) {
return(uiABRef);
}
IAB_Neuter(lpIAB);
bSetOLKAllocators = lpIAB->bSetOLKAllocators;
FreeBufferAndNull(&lpIAB);
// [PaulHi] 5/5/99 Raid 77138 Null out Outlook allocator function
// pointers if our global count goes to zero.
if (bSetOLKAllocators)
{
Assert(g_nExtMemAllocCount > 0);
InterlockedDecrement((LPLONG)&g_nExtMemAllocCount);
if (g_nExtMemAllocCount == 0)
{
lpfnAllocateBufferExternal = NULL;
lpfnAllocateMoreExternal = NULL;
lpfnFreeBufferExternal = NULL;
}
}
return(0);
}
/*
* IAB_Neuter
*
* Purpose
* Destroys the memory and objects inside the address book object, leaving
* nothing but the object itself to be freed. Called from CleanupSession()
* in isess.c (client-side cleanup) and from SplsessRelease() in splsess.c
* (spooler-side cleanup). Note that the address book object is only taken
* down as a part of session takedown. Once this call is made, we can assume
* the session is shutting down and can free up everything guilt-free
*/
void
IAB_Neuter(LPIAB lpIAB)
{
HINSTANCE hinst = hinstMapiXWAB;
WNDCLASS wc;
if (lpIAB == NULL) {
TraceSz( TEXT("IAB_Neuter: given a NULL lpIAB"));
return;
}
// clean up the advise list for the AB
#ifdef OLD_STUFF
if (lpIAB->padviselistIAB) {
DestroyAdviseList(&lpIAB->padviselistIAB);
}
#endif // OLD_STUFF
// Get rid of any cached context menu extension data
UlRelease(lpIAB->lpCntxtMailUser);
//
// Get rid of my PropData
//
UlRelease(lpIAB->lpPropData);
//
// Get rid of any EntryIDs I've got floating around
//
FreeBufferAndNull(&(lpIAB->lpEntryIDDD));
lpIAB->lpEntryIDDD = NULL;
FreeBufferAndNull(&(lpIAB->lpEntryIDPAB));
lpIAB->lpEntryIDPAB = NULL;
// Remove the SearchPath cache
#if defined (WIN32) && !defined (MAC)
if (fGlobalCSValid) {
EnterCriticalSection(&csMapiSearchPath);
} else {
DebugTrace( TEXT("IAB_Neuter: WAB32.DLL already detached.\n"));
}
#endif
FreeBufferAndNull(&(lpIAB->lpspvSearchPathCache));
lpIAB->lpspvSearchPathCache = NULL;
#if defined (WIN32) && !defined (MAC)
if (fGlobalCSValid) {
LeaveCriticalSection(&csMapiSearchPath);
} else {
DebugTrace(TEXT("IAB_Neuter: WAB32.DLL got detached.\n"));
}
#endif
//
// Release any MAPI allocated error structure
//
FreeBufferAndNull(&(lpIAB->lpMAPIError));
// Release the property store associated with the IAB
ReleasePropertyStore(lpIAB->lpPropertyStore);
// Unload LDAP client if it was loaded at IAB creation.
//if (lpIAB->fLoadedLDAP)
{
DeinitLDAPClientLib();
}
if ((NULL != lpIAB->hWndNotify) && (FALSE != IsWindow(lpIAB->hWndNotify))) {
SendMessage(lpIAB->hWndNotify, WM_CLOSE, 0, 0);
}
// On Windows NT/2000: No window classes registered by a .dll
// are unregistered when the .dll is unloaded.
// If we dont do this we might point to an old/invalid IABNotifWndProc
// pointer in CreateIABNotificationTimer
if (GetClassInfo(hinst, szWABNotifClassName, &wc)) {
UnregisterClass(szWABNotifClassName, hinst);
}
lpIAB->hWndNotify = NULL;
lpIAB->ulNotifyTimer = 0;
// Terminate the wab file change notification thread
if (lpIAB->hThreadNotify != INVALID_HANDLE_VALUE)
{
DWORD dwRtn;
// Signal thread to terminate and wait
Assert(lpIAB->hEventKillNotifyThread);
SetEvent(lpIAB->hEventKillNotifyThread);
dwRtn = WaitForSingleObject(lpIAB->hThreadNotify, INFINITE);
CloseHandle(lpIAB->hThreadNotify);
lpIAB->hThreadNotify = INVALID_HANDLE_VALUE;
}
if (lpIAB->lpwszWABFilePath)
LocalFreeAndNull(&(lpIAB->lpwszWABFilePath));
if (lpIAB->hEventKillNotifyThread)
{
CloseHandle(lpIAB->hEventKillNotifyThread);
lpIAB->hEventKillNotifyThread = NULL;
}
while( lpIAB->pWABAdviseList && //shouldnt happen
lpIAB->pWABAdviseList->cAdvises &&
lpIAB->pWABAdviseList->lpNode)
{
HrUnadvise(lpIAB, lpIAB->pWABAdviseList->lpNode->ulConnection);
}
// Free any memory allocated to rt-click action items
//
if(lpIAB->lpActionList)
FreeActionItemList(lpIAB);
if(lpIAB->lpPropExtDllList)
FreePropExtList(lpIAB->lpPropExtDllList);
FreeWABFoldersList(lpIAB);
FreeProfileContainerInfo(lpIAB);
UninitExtInfo();
UninitContextExtInfo();
// Release the account manager
UninitAccountManager();
// Release the identity manager
HrRegisterUnregisterForIDNotifications( lpIAB, FALSE);
UninitUserIdentityManager(lpIAB);
if(lpIAB->hKeyCurrentUser)
RegCloseKey(lpIAB->hKeyCurrentUser);
// Release trident
UninitTrident();
UIOLEUninit();
//
// Set the time-bomb
//
lpIAB->lpVtbl = NULL;
DeleteCriticalSection(&lpIAB->cs);
if(lpIAB->hMutexOlk)
CloseHandle(lpIAB->hMutexOlk);
return;
}
// IMAPIProp
/**************************************************
*
* IAB_GetLastError()
*
* Returns a string associated with the last hResult
* returned by the IAB object.
*
* Now UNICODE enabled
*
*/
STDMETHODIMP
IAB_GetLastError(LPIAB lpIAB,
HRESULT hError,
ULONG ulFlags,
LPMAPIERROR FAR * lppMAPIError)
{
HRESULT hr = hrSuccess;
#ifdef PARAMETER_VALIDATION
// Check to see if it has a jump table
if (IsBadReadPtr(lpIAB, sizeof(LPVOID))) {
// No jump table found
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
// Check to see if the jump table has at least sizeof IUnknown
if (IsBadReadPtr(lpIAB->lpVtbl, 4*sizeof(LPVOID))) {
// Jump table not derived from IUnknown
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
// Check to see if the method is the same
if (IAB_GetLastError != lpIAB->lpVtbl->GetLastError) {
// Wrong object - the object passed doesn't have this
// method.
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (FBadGetLastError(lpIAB, hError, ulFlags, lppMAPIError)) {
DebugTraceArg(IAB_GetLastError, TEXT("Bad writeable parameter"));
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (ulFlags & ~MAPI_UNICODE) {
DebugTraceArg(IAB_GetLastError, TEXT("reserved flags used"));
// return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpIAB->cs);
if(lppMAPIError)
*lppMAPIError = NULL; // really isnt anything to return here for now
// set this to NULL, which means no error information
#ifdef OLD_STUFF
hr = HrGetLastError(lpIAB, hError, ulFlags, lppMAPIError);
#endif
LeaveCriticalSection(&lpIAB->cs);
return(hr);
}
//
// Set of properties set by Creating a one off.
//
enum {
iooPR_ADDRTYPE = 0,
iooPR_DISPLAY_NAME,
iooPR_EMAIL_ADDRESS,
iooPR_ENTRYID,
iooPR_OBJECT_TYPE,
iooMax
};
/***************************************************************************
Name : IsOneOffEID
Purpose : Is this EntryID a One-off?
Parameters: cbEntryID = size of lpEntryID
lpEntryID -> entry ID of one off to open
Returns : TRUE or FALSE
Comment :
***************************************************************************/
BOOL IsOneOffEID(ULONG cbEntryID, LPENTRYID lpEntryID) {
return(WAB_ONEOFF == IsWABEntryID(cbEntryID, lpEntryID, NULL, NULL, NULL, NULL, NULL));
}
/***************************************************************************
Name : NewOneOff
Purpose : Create a new MailUser object based on a OneOff EntryID
Parameters: cbEntryID = size of lpEntryID
lpEntryID -> entry ID of one off to open
lpulObjType -> returned object type
Returns : HRESULT
Comment : OneOff EID format is MAPI_ENTRYID:
BYTE abFlags[4];
MAPIUID mapiuid; // = WABONEOFFEID
BYTE bData[]; // contains szaddrtype followed by szaddress
// the delimiter is the null after szaddrtype.
// The first ULONG in bData[] is the ulMapiDataType,
// which contains the MAPI_UNICODE flag if unicode.
Assumes that the EntryID contains valid strings. It is the job of
the caller to validate the EntryID before calling NewOneOff.
***************************************************************************/
HRESULT NewOneOff(
LPIAB lpIAB,
ULONG cbEntryID,
LPENTRYID lpEntryID,
LPULONG lpulObjType,
LPUNKNOWN FAR * lppUnk)
{
HRESULT hResult = hrSuccess;
LPMAPI_ENTRYID lpMapiEID = (LPMAPI_ENTRYID)lpEntryID;
LPMAILUSER lpMailUser = NULL;
SPropValue spv[iooMax];
LPBYTE lpbDisplayName, lpbAddrType, lpbAddress;
LPTSTR lptszDisplayName = NULL;
LPTSTR lptszAddrType = NULL;
LPTSTR lptszAddress = NULL;
LPPROPDATA lpPropData = NULL;
ULONG ulMapiDataType = 0;
// Validate the EntryID as WAB_ONEOFF
if (WAB_ONEOFF != IsWABEntryID(cbEntryID, lpEntryID, &lpbDisplayName, &lpbAddrType, &lpbAddress, (LPVOID *)&ulMapiDataType, NULL)) {
hResult = ResultFromScode(MAPI_E_INVALID_ENTRYID);
goto exit;
}
// [PaulHi] 1/20/99 Raid 64211
// UNICODE is native in WAB. Convert strings to be consistent
if (!(ulMapiDataType & MAPI_UNICODE))
{
lptszDisplayName = ConvertAtoW((LPSTR)lpbDisplayName);
lptszAddrType = ConvertAtoW((LPSTR)lpbAddrType);
lptszAddress = ConvertAtoW((LPSTR)lpbAddress);
}
else
{
lptszDisplayName = (LPTSTR)lpbDisplayName;
lptszAddrType = (LPTSTR)lpbAddrType;
lptszAddress = (LPTSTR)lpbAddress;
}
// Parse the addrtype and address out of the entryid
// DebugTrace(TEXT("NewOneOff: [%s:%s:%s]\n"), lpDisplayName, lpAddrType, lpAddress);
// Create a new MAILUSER object
if (HR_FAILED(hResult = HrNewMAILUSER(lpIAB, NULL, MAPI_MAILUSER, 0, &lpMailUser))) {
goto exit;
}
lpPropData = ((LPMailUser)lpMailUser)->lpPropData;
// Fill it with properties... we only have a few.
// PR_OBJECT_TYPE
spv[iooPR_ADDRTYPE].ulPropTag = PR_ADDRTYPE;
spv[iooPR_ADDRTYPE].Value.LPSZ = lptszAddrType;
spv[iooPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME;
spv[iooPR_DISPLAY_NAME].Value.LPSZ = lptszDisplayName;
spv[iooPR_EMAIL_ADDRESS].ulPropTag = PR_EMAIL_ADDRESS;
spv[iooPR_EMAIL_ADDRESS].Value.LPSZ = lptszAddress;
spv[iooPR_ENTRYID].ulPropTag = PR_ENTRYID;
spv[iooPR_ENTRYID].Value.bin.lpb = (LPBYTE)lpEntryID;
spv[iooPR_ENTRYID].Value.bin.cb = cbEntryID;
// BUGBUG: This is already done in HrNewMAILUSER.
spv[iooPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE;
spv[iooPR_OBJECT_TYPE].Value.l = MAPI_MAILUSER;
Assert(lpMailUser);
// Set the properties
if (HR_FAILED(hResult = lpPropData->lpVtbl->SetProps(lpPropData,
iooMax, // number of properties to set
spv, // property array
NULL))) { // problem array
goto exit;
}
*lpulObjType = MAPI_MAILUSER;
*lppUnk = (LPUNKNOWN)lpMailUser;
exit:
if (!(ulMapiDataType & MAPI_UNICODE))
{
LocalFreeAndNull(&lptszDisplayName);
LocalFreeAndNull(&lptszAddrType);
LocalFreeAndNull(&lptszAddress);
}
if (HR_FAILED(hResult)) {
FreeBufferAndNull(&lpMailUser);
}
return(hResult);
}
typedef enum {
e_IMailUser,
e_IDistList,
e_IABContainer,
e_IMAPIContainer,
e_IMAPIProp,
} INTERFACE_INDEX;
/***************************************************************************
Name : HrAddPrSearchKey
Purpose : Dynamically creates a PR_SEARCH_KEY and adds it to the object.
Parameters: lppUnk -> pointer to mailuser object
Returns : HRESULT
Comment : No UNICODE flags.
***************************************************************************/
HRESULT HrAddPrSearchKey(LPUNKNOWN FAR * lppUnk,
ULONG cbEntryID,
LPENTRYID lpEntryID)
{
HRESULT hr = E_FAIL;
ULONG ulcProps = 0;
LPSPropValue lpPropArray = NULL;
LPMAILUSER lpMailUser = NULL;
LPSPropValue lpPropArrayNew = NULL;
ULONG ulcPropsNew = 0;
ULONG i = 0;
SCODE sc;
ULONG ulObjAccess = 0;
LPIPDAT lpPropData = NULL;
if(!lppUnk || !(*lppUnk))
{
hr = MAPI_E_INVALID_PARAMETER;
goto exit;
}
lpMailUser = (LPMAILUSER) (*lppUnk);
lpPropData = (LPIPDAT) ((LPMailUser) lpMailUser)->lpPropData;
//temporarily overwrite the object access so we can modify it here
ulObjAccess = lpPropData->ulObjAccess;
lpPropData->ulObjAccess = IPROP_READWRITE;
hr = lpMailUser->lpVtbl->GetProps( lpMailUser,
NULL,
MAPI_UNICODE,
&ulcProps,
&lpPropArray);
if(HR_FAILED(hr))
goto exit;
if (ulcProps && lpPropArray)
{
// 4/14/97 - vikramm
// Outlook expects a PR_SEARCH_KEY on each mailuser or distlist
// This is a dynamic property created at runtime - ideally, if we're
// running against outlook store, we should have one at this point
// but to be consistent, if we dont have one we should add one
// The PR_SEARCH_KEY is a binary property which is made of the
// email address or if there is no email address, is the entryid of
// this contact ..
{
SPropValue PropSearchKey = {0};
LPTSTR lpszEmail = NULL;
LPTSTR lpszAddrType = NULL;
BOOL bSearchKeyFound = FALSE;
for (i = 0; i < ulcProps; i++)
{
switch(lpPropArray[i].ulPropTag)
{
case PR_EMAIL_ADDRESS:
lpszEmail = lpPropArray[i].Value.LPSZ;
break;
case PR_ADDRTYPE:
lpszAddrType = lpPropArray[i].Value.LPSZ;
break;
case PR_SEARCH_KEY:
bSearchKeyFound = TRUE;
break;
}
}
if(!bSearchKeyFound)
{
PropSearchKey.ulPropTag = PR_SEARCH_KEY;
//Create a search key
if(lpszEmail && lpszAddrType)
{
// Search Key is based on email address
// [PaulHi] 4/23/99 Raid 76717
// The Search Key strings must be single byte for Outlook. Do conversion here.
{
LPSTR lpszKey;
DWORD cchSize = (lstrlen(lpszAddrType) + 1 + lstrlen(lpszEmail) + 1);
LPWSTR lpwszKey = LocalAlloc( LMEM_ZEROINIT, (sizeof(WCHAR)*cchSize));
if (!lpwszKey)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto exit;
}
StrCpyN(lpwszKey, lpszAddrType, cchSize);
StrCatBuff(lpwszKey, szColon, cchSize);
StrCatBuff(lpwszKey, lpszEmail, cchSize);
// This search key should be in upper case
CharUpper(lpwszKey);
lpszKey = ConvertWtoA(lpwszKey);
LocalFreeAndNull(&lpwszKey);
if (!lpszKey)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto exit;
}
PropSearchKey.Value.bin.cb = (lstrlenA(lpszKey) + 1);
PropSearchKey.Value.bin.lpb = (LPBYTE)lpszKey;
}
}
else
{
// Search key is based on entry id
if(!cbEntryID || !lpEntryID)
{
hr = MAPI_E_INVALID_PARAMETER;
goto exit;
}
PropSearchKey.Value.bin.cb = cbEntryID;
PropSearchKey.Value.bin.lpb = (LPBYTE) lpEntryID;
}
// Add this search key to the proparray
sc = ScMergePropValues( 1,
&PropSearchKey,
ulcProps,
lpPropArray,
&ulcPropsNew,
&lpPropArrayNew);
// Free this pointer if it was allocated
if(PropSearchKey.Value.bin.lpb != (LPBYTE) lpEntryID)
LocalFree(PropSearchKey.Value.bin.lpb);
if (sc != S_OK)
{
hr = ResultFromScode(sc);
goto exit;
}
}
}
if (HR_FAILED(hr = lpMailUser->lpVtbl->SetProps(lpMailUser,
(lpPropArrayNew ? ulcPropsNew : ulcProps), // number of properties to set
(lpPropArrayNew ? lpPropArrayNew : lpPropArray), // property array
NULL))) // problem array
{
goto exit;
}
}
exit:
// reset the object access
if(!HR_FAILED(hr) &&
(ulObjAccess != lpPropData->ulObjAccess))
{
lpPropData->ulObjAccess = ulObjAccess;
}
if(lpPropArrayNew)
MAPIFreeBuffer(lpPropArrayNew);
if(lpPropArray)
MAPIFreeBuffer(lpPropArray);
return hr;
}
/***************************************************************************
Name : IADDRBOOK_GetIDsFromNames
Returns : HRESULT
Comment :
***************************************************************************/
STDMETHODIMP
IAB_GetIDsFromNames(LPIAB lpIAB, ULONG cPropNames, LPMAPINAMEID * lppPropNames,
ULONG ulFlags, LPSPropTagArray * lppPropTags)
{
#if !defined(NO_VALIDATION)
// Make sure the object is valid.
if (BAD_STANDARD_OBJ(lpIAB, IAB_, GetIDsFromNames, lpVtbl)) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
#endif
VerifyWABOpenExSession(lpIAB);
return HrGetIDsFromNames(lpIAB,
cPropNames,
lppPropNames, ulFlags, lppPropTags);
}
/***************************************************************************
Name : IADDRBOOK::OpenEntry
Purpose : Asks the appropriate provider to give a appropriate object
relevant to the given lpEntryID.
Parameters: lpIAB -> this addrbook object
cbEntryID = size of lpEntryID
lpEntryID -> entry ID of one off to open
lpInterface -> requested interface
ulFlags = flags
lpulObjType -> returned object type
lppUnk -> returned object
Returns : HRESULT
Comment : A special case is the One-Off Provider. There is no *real*
provider associated with One-Off entry IDs. Still though,
we'll try to treat them much the same as any other provider.
No UNICODE flags.
***************************************************************************/
STDMETHODIMP
IAB_OpenEntry(LPIAB lpIAB,
ULONG cbEntryID,
LPENTRYID lpEntryID,
LPCIID lpInterface,
ULONG ulFlags,
ULONG FAR * lpulObjType,
LPUNKNOWN FAR * lppUnk)
{
HRESULT hr = hrSuccess;
LPMAILUSER lpMailUser = NULL;
LPMAPIPROP lpMapiProp = NULL;
ULONG ulcProps = 0;
LPSPropValue lpPropArray = NULL;
ULONG i;
ULONG ulType;
INTERFACE_INDEX ii = e_IMailUser;
SCODE sc;
#ifndef DONT_ADDREF_PROPSTORE
if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpIAB->lpPropertyStore)))) {
hr = ResultFromScode(sc);
goto exitNotAddRefed;
}
#endif
#ifdef PARAMETER_VALIDATION
//
// Parameter Validataion
//
// Is this one of mine??
if (IsBadReadPtr(lpIAB, sizeof(IAB))) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (lpIAB->lpVtbl != &vtblIAB) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (lpInterface && IsBadReadPtr(lpInterface, sizeof(IID))) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (ulFlags & ~(MAPI_MODIFY | MAPI_DEFERRED_ERRORS | MAPI_BEST_ACCESS)) {
DebugTraceArg(IAB_OpenEntry , TEXT("Unknown flags used"));
// return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
if (IsBadWritePtr(lpulObjType, sizeof(ULONG))) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (IsBadWritePtr(lppUnk, sizeof(LPUNKNOWN))) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
#endif // PARAMETER_VALIDATION
VerifyWABOpenExSession(lpIAB);
EnterCriticalSection(&lpIAB->cs);
//
// First check to see if it's NULL - if so, it is our ROOT container
//
if (! lpEntryID) {
hr = HrNewCONTAINER(lpIAB,
AB_ROOT, // ulType
lpInterface,
ulFlags,
cbEntryID,
lpEntryID,
lpulObjType,
lppUnk);
goto exit;
}
switch (IsWABEntryID(cbEntryID, lpEntryID, NULL, NULL, NULL, NULL, NULL)) {
case WAB_PABSHARED:
case WAB_PAB:
hr = HrNewCONTAINER(lpIAB,
AB_PAB, // ulType
lpInterface,
ulFlags,
cbEntryID,
lpEntryID,
lpulObjType,
lppUnk);
goto exit;
case WAB_CONTAINER:
hr = HrNewCONTAINER(lpIAB,
AB_CONTAINER,
lpInterface,
ulFlags,
cbEntryID,
lpEntryID,
lpulObjType,
lppUnk);
goto exit;
case WAB_LDAP_CONTAINER:
// Check if the EntryID is for the WAB's PAB container.
hr = HrNewCONTAINER(lpIAB,
AB_LDAP_CONTAINER, // ulType
lpInterface,
ulFlags,
cbEntryID,
lpEntryID,
lpulObjType,
lppUnk);
goto exit;
case WAB_LDAP_MAILUSER:
hr = LDAP_OpenMAILUSER(lpIAB,
cbEntryID,
lpEntryID,
lpInterface,
ulFlags,
lpulObjType,
lppUnk);
if(!HR_FAILED(hr))
{
hr = HrAddPrSearchKey(lppUnk, cbEntryID, lpEntryID);
}
goto exit;
}
// Check for One-Off
if (IsOneOffEID(cbEntryID, lpEntryID)) {
// Create a one-off mailuser object
hr = NewOneOff(lpIAB,
cbEntryID,
lpEntryID,
lpulObjType,
lppUnk);
if(!HR_FAILED(hr))
{
hr = HrAddPrSearchKey(lppUnk, cbEntryID, lpEntryID);
}
goto exit;
}
//
// Not NULL, is it ours?
//
// assume it is ours ..
{
SBinary sbEID = {0};
sbEID.cb = cbEntryID;
sbEID.lpb = (LPBYTE) lpEntryID;
// What interface was requested?
// We've basically got 2 interfaces here... IMailUser and IDistList.
if (lpInterface != NULL) {
if (! memcmp(lpInterface, &IID_IMailUser, sizeof(IID))) {
ii = e_IMailUser;
} else if (! memcmp(lpInterface, &IID_IDistList, sizeof(IID))) {
ii = e_IDistList;
} else if (! memcmp(lpInterface, &IID_IABContainer, sizeof(IID))) {
ii = e_IABContainer;
} else if (! memcmp(lpInterface, &IID_IMAPIContainer, sizeof(IID))) {
ii = e_IMAPIContainer;
} else if (! memcmp(lpInterface, &IID_IMAPIProp, sizeof(IID))) {
ii = e_IMAPIProp;
} else {
hr = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
goto exit;
}
}
/*** Bug:31975 - dont default to mail user
else {
ii = e_IMailUser;
}
*/
Assert(lpIAB->lpPropertyStore->hPropertyStore);
if (HR_FAILED(hr = ReadRecord(lpIAB->lpPropertyStore->hPropertyStore,
&sbEID, // EntryID
0, // ulFlags
&ulcProps, // number of props returned
&lpPropArray))) { // properties returned
DebugTraceResult(IAB_OpenEntry:ReadRecord, hr);
goto exit;
}
ulType = MAPI_MAILUSER; // Default
if (ulcProps) {
Assert(lpPropArray);
if (lpPropArray) {
// Look for PR_OBJECT_TYPE
for (i = 0; i < ulcProps; i++) {
if (lpPropArray[i].ulPropTag == PR_OBJECT_TYPE) {
ulType = lpPropArray[i].Value.l;
break;
}
}
}
}
/*** Bug 31975 - dont default to mailuser **/
if(!lpInterface)
{
ii = (ulType == MAPI_DISTLIST) ? e_IDistList : e_IMailUser;
}
switch (ulType) {
case MAPI_MAILUSER:
case MAPI_ABCONT:
switch (ii) {
case e_IMailUser:
case e_IMAPIContainer:
case e_IMAPIProp:
// Create a new MAILUSER object
if (HR_FAILED(hr = HrNewMAILUSER(lpIAB,
NULL,
MAPI_MAILUSER,
0,
&lpMapiProp)))
{
goto exit;
}
HrSetMAILUSERAccess((LPMAILUSER)lpMapiProp, MAPI_MODIFY);
break;
default:
hr = ResultFromScode(MAPI_E_INTERFACE_NOT_SUPPORTED);
goto exit;
}
break;
case MAPI_DISTLIST:
switch (ii) {
case e_IMailUser:
case e_IMAPIProp:
// Create a new MAILUSER object
if (HR_FAILED(hr = HrNewMAILUSER(lpIAB,
NULL,
ulType,
0,
&lpMapiProp))) {
goto exit;
}
HrSetMAILUSERAccess((LPMAILUSER)lpMapiProp, MAPI_MODIFY);
break;
case e_IDistList:
case e_IABContainer:
case e_IMAPIContainer:
// Create the Distribution List object.
if (HR_FAILED(hr = HrNewCONTAINER(lpIAB,
AB_DL, // ulType
lpInterface,
ulFlags,
0,
NULL,
lpulObjType,
&lpMapiProp))) {
goto exit;
}
HrSetCONTAINERAccess((LPCONTAINER)lpMapiProp, MAPI_MODIFY);
break;
default:
Assert(FALSE);
}
break;
default:
{
//Assert(FALSE);
// Most likely if we got here we got an object of type MAPI_ABCONT somehow..
// better to fail here gracefully than to barf and assert and then crash
hr = MAPI_E_INVALID_OBJECT;
goto exit;
}
break;
}
if (ulcProps && lpPropArray)
{
LPPROPDATA lpPropData = NULL;
// If the entry had properties, set them in our returned object
lpPropData = ((LPMailUser)lpMapiProp)->lpPropData;
if (HR_FAILED(hr = lpPropData->lpVtbl->SetProps(lpPropData,
ulcProps, // number of properties to set
lpPropArray, // property array
NULL))) // problem array
{
goto exit;
}
}
switch (ulType) {
case MAPI_MAILUSER:
HrSetMAILUSERAccess((LPMAILUSER)lpMapiProp, ulFlags);
break;
case MAPI_DISTLIST:
HrSetCONTAINERAccess((LPCONTAINER)lpMapiProp, ulFlags);
break;
}
*lpulObjType = ulType;
*lppUnk = (LPUNKNOWN)lpMapiProp;
if(!HR_FAILED(hr))
{
hr = HrAddPrSearchKey(lppUnk, cbEntryID, lpEntryID);
}
goto exit; // success
}
hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
exit:
#ifndef DONT_ADDREF_PROPSTORE
ReleasePropertyStore(lpIAB->lpPropertyStore);
exitNotAddRefed:
#endif
ReadRecordFreePropArray( lpIAB->lpPropertyStore->hPropertyStore,
ulcProps,
&lpPropArray); // Free memory from ReadRecord
LeaveCriticalSection(&lpIAB->cs);
DebugTraceResult(IAB_OpenEntry, hr);
return(hr);
}
/**************************************************
*
* IADRBOOK::CreateOneOff()
*
* Creates an entry ID which has all the information
* about this entry contained within it.
*
* Notes:
* I need a MAPIUID. For now, I'll make one up.
* The ulDataType is UNICODE. I'll define that in
* _abint.h for now.
*
* Now UNICODE enabled. If the MAPI_UNICODE flag is set
* the text elements of the EntryID will be WCHAR.
*/
STDMETHODIMP
IAB_CreateOneOff(LPIAB lpIAB,
LPTSTR lpszName,
LPTSTR lpszAdrType,
LPTSTR lpszAddress,
ULONG ulFlags,
ULONG * lpcbEntryID,
LPENTRYID * lppEntryID)
{
HRESULT hr = hrSuccess;
BOOL bIsUnicode = (ulFlags & MAPI_UNICODE) == MAPI_UNICODE;
LPTSTR lpName = NULL, lpAdrType = NULL, lpAddress = NULL;
if(!bIsUnicode) // <note> assumes UNICODE defined
{
lpName = ConvertAtoW((LPSTR)lpszName);
lpAdrType = ConvertAtoW((LPSTR)lpszAdrType);
lpAddress = ConvertAtoW((LPSTR)lpszAddress);
}
else
{
lpName = lpszName;
lpAdrType = lpszAdrType;
lpAddress = lpszAddress;
}
hr = CreateWABEntryIDEx(bIsUnicode,
WAB_ONEOFF,
lpName,
lpAdrType,
lpAddress,
0, 0,
NULL,
lpcbEntryID,
lppEntryID);
if(!bIsUnicode) // <note> assumes UNICODE defined
{
LocalFreeAndNull(&lpName);
LocalFreeAndNull(&lpAdrType);
LocalFreeAndNull(&lpAddress);
}
return(hr);
}
STDMETHODIMP
IAB_CompareEntryIDs(LPIAB lpIAB,
ULONG cbEntryID1,
LPENTRYID lpEntryID1,
ULONG cbEntryID2,
LPENTRYID lpEntryID2,
ULONG ulFlags,
ULONG * lpulResult)
{
LPMAPI_ENTRYID lpMapiEid1 = (LPMAPI_ENTRYID) lpEntryID1;
LPMAPI_ENTRYID lpMapiEid2 = (LPMAPI_ENTRYID) lpEntryID2;
HRESULT hr = hrSuccess;
#ifdef PARAMETER_VALIDATION
//
// Parameter Validataion
//
// Is this one of mine??
if (IsBadReadPtr(lpIAB, sizeof(IAB)))
{
//return(ReportResult(0, MAPI_E_INVALID_PARAMETER, 0, 0));
DebugTrace(TEXT("ERROR: IAB_CompareEntryIDs - invalid lpIAB"));
return (MAPI_E_INVALID_PARAMETER);
}
if (lpIAB->lpVtbl != &vtblIAB)
{
//return(ReportResult(0, MAPI_E_INVALID_PARAMETER, 0, 0));
DebugTrace(TEXT("ERROR: IAB_CompareEntryIDs - invalid lpIAB Vtable"));
return (MAPI_E_INVALID_PARAMETER);
}
// ulFlags must be 0
if (ulFlags)
{
//return(ReportResult(0, MAPI_E_UNKNOWN_FLAGS, 0, 0));
DebugTrace(TEXT("WARNING: IAB_CompareEntryIDs - invalid flag parameter"));
// No need to return error
}
if (IsBadWritePtr(lpulResult, sizeof(ULONG)))
{
//return(ReportResult(0, MAPI_E_INVALID_PARAMETER, 0, 0));
DebugTrace(TEXT("ERROR: IAB_CompareEntryIDs - invalid out pointer"));
return (MAPI_E_INVALID_PARAMETER);
}
// NULL EIDs are OK
if ( cbEntryID1 && lpEntryID1 &&
IsBadReadPtr(lpEntryID1, cbEntryID1) )
{
DebugTrace(TEXT("ERROR: IAB_CompareEntryIDs - invalid EntryID1"));
return (MAPI_E_INVALID_PARAMETER);
}
if ( cbEntryID2 && lpEntryID2 &&
IsBadReadPtr(lpEntryID2, cbEntryID2))
{
DebugTrace(TEXT("ERROR: IAB_CompareEntryIDs - invalid EntryID2"));
return (MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpIAB->cs);
*lpulResult = FALSE; // default
// Optimization, see if they're binarily the same
if (cbEntryID1 == cbEntryID2) {
if (cbEntryID1 && 0 == memcmp((LPVOID) lpMapiEid1, (LPVOID) lpMapiEid2,
(size_t) cbEntryID1)) {
//
// They've got to be the same
//
*lpulResult = TRUE;
hr = hrSuccess;
goto exit;
}
}
exit:
LeaveCriticalSection(&lpIAB->cs);
return(hr);
}
//-----------------------------------------------------------------------------
// Synopsis: IAB_Advise()
// Description:
//
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//-----------------------------------------------------------------------------
STDMETHODIMP
IAB_Advise(LPIAB lpIAB,
ULONG cbEntryID,
LPENTRYID lpEntryID,
ULONG ulEventMask,
LPMAPIADVISESINK lpAdvise,
ULONG FAR * lpulConnection)
{
HRESULT hr = hrSuccess;
SCODE sc = S_OK;
LPSTR lpszError = NULL;
LPSTR lpszComponent = NULL;
// LPMAPI_ENTRYID lpMapiEid = (LPMAPI_ENTRYID) lpEntryID;
// LPLSTPROVDATA lpProvData = NULL;
// LPABLOGON lpABLogon = NULL;
// The basic advise implementation ignores the entryids and only looks
// for event masks of type OBjectModified. The only notifications fired
// are for any changes in the WAB store...
//
if (! ulEventMask || !(ulEventMask & fnevObjectModified))
return MAPI_E_INVALID_PARAMETER;
hr = HrAdvise(lpIAB,
cbEntryID,
lpEntryID,
ulEventMask,
lpAdvise,
lpulConnection);
return(hr);
}
//-----------------------------------------------------------------------------
// Synopsis: IAB_Unadvise()
// Description:
//
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//-----------------------------------------------------------------------------
STDMETHODIMP
IAB_Unadvise (LPIAB lpIAB, ULONG ulConnection)
{
HRESULT hr = hrSuccess;
SCODE sc = S_OK;
hr = HrUnadvise(lpIAB, ulConnection);
return(hr);
}
//
// Interesting table columns
//
enum {
ifePR_CONTACT_EMAIL_ADDRESSES = 0,
ifePR_EMAIL_ADDRESS,
ifePR_DISPLAY_NAME,
ifePR_OBJECT_TYPE,
ifePR_USER_X509_CERTIFICATE,
ifePR_ENTRYID,
ifePR_SEARCH_KEY,
ifeMax
};
static const SizedSPropTagArray(ifeMax, ptaFind) =
{
ifeMax,
{
PR_CONTACT_EMAIL_ADDRESSES,
PR_EMAIL_ADDRESS,
PR_DISPLAY_NAME,
PR_OBJECT_TYPE,
PR_USER_X509_CERTIFICATE,
PR_ENTRYID,
PR_SEARCH_KEY
}
};
/***************************************************************************
Name : HrRowToADRENTRY
Purpose : Gets the next row from a table and places it in the ADRENTRY
Parameters: lpIAB -> Address book object
lpTable -> table object
lpAdrEntry = ADRENTRY to fill
Returns : HRESULT
Comment :
***************************************************************************/
HRESULT HrRowToADRENTRY(LPIAB lpIAB, LPMAPITABLE lpTable, LPADRENTRY lpAdrEntry, BOOL bUnicode) {
HRESULT hResult;
LPSRowSet lpRow = NULL;
SCODE sc;
LPMAPIPROP lpMailUser = NULL;
LPSPropValue lpPropArray = NULL;
LPSPropValue lpPropArrayNew = NULL;
ULONG ulObjType, cValues, cPropsNew;
if (hResult = lpTable->lpVtbl->QueryRows(lpTable,
1, // First row only
0, // ulFlags
&lpRow)) {
DebugTrace(TEXT("GetNextRowEID:QueryRows -> %x\n"), GetScode(hResult));
} else {
// Found it, copy entryid to new allocation
if (lpRow->cRows) {
if (HR_FAILED(hResult = lpIAB->lpVtbl->OpenEntry(lpIAB,
lpRow->aRow[0].lpProps[ifePR_ENTRYID].Value.bin.cb, // cbEntryID
(LPENTRYID)lpRow->aRow[0].lpProps[ifePR_ENTRYID].Value.bin.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,
(LPSPropTagArray)&ptaResolveDefaults, // lpPropTagArray
(bUnicode ? MAPI_UNICODE : 0), // ulFlags
&cValues, // how many properties were there?
&lpPropArray))) {
DebugTraceResult( TEXT("ResolveNames GetProps"), hResult);
goto exit;
}
hResult = hrSuccess;
// 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)) { // dest
goto exit;
}
// [PaulHi] 2/1/99
// GetProps now only returns requested property strings in the requested
// format (UNICODE or ANSI). If the client calls this function without
// the MAPI_UNICODE flag then ensure all string properties are converted
// to ANSI.
//
// @review 2/1/99 These ScConvertWPropsToA conversions are expensive,
// since there currently is no "MAPIRealloc" function and the original
// string memory remains allocated until the props array is deallocated.
if (!bUnicode)
{
if(sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpPropArrayNew, cPropsNew, 0))
goto exit;
}
// Free the original prop value array
FreeBufferAndNull((LPVOID *) (&(lpAdrEntry->rgPropVals)));
lpAdrEntry->cValues = cPropsNew;
lpAdrEntry->rgPropVals = lpPropArrayNew;
FreeBufferAndNull(&lpPropArray);
} else {
hResult = ResultFromScode(MAPI_E_NOT_FOUND);
}
}
exit:
if (lpMailUser) {
UlRelease(lpMailUser);
}
if (lpRow) {
FreeProws(lpRow);
}
return(hResult);
}
/***************************************************************************
Name : InitPropertyRestriction
Purpose : Fills in the property restriction structure
Parameters: lpsres -> SRestriction to fill in
lpspv -> property value structure for this property restriction
Returns : none
Comment :
***************************************************************************/
void InitPropertyRestriction(LPSRestriction lpsres, LPSPropValue lpspv) {
lpsres->rt = RES_PROPERTY; // Restriction type Property
lpsres->res.resProperty.relop = RELOP_EQ;
lpsres->res.resProperty.ulPropTag = lpspv->ulPropTag;
lpsres->res.resProperty.lpProp = lpspv;
}
/***************************************************************************
Name : HrSmartResolve
Purpose : Goes to great lengths to single out contacts in the local WAB.
Parameters: lpIAB = adrbook object
lpContainer = container to search
ulFlags = flags passed to ResolveName
lpAdrList -> [in/out] ADRLIST
lpFlagList -> flags corresponding to adrlist
lpAmbiguousTables -> ambiguity dialog information
Returns : HRESULT
Comment : Assumes that the container's ResolveNames method has already
been called and has filled in lpFlagList. This routine goes
the extra mile to find email addresses and to resolve
ambiguity.
When we get here, we can assume that the DisplayName was
either not found or was ambiguous.
***************************************************************************/
HRESULT HrSmartResolve(LPIAB lpIAB, LPABCONT lpContainer, ULONG ulFlags,
LPADRLIST lpAdrList, LPFlagList lpFlagList, LPAMBIGUOUS_TABLES lpAmbiguousTables) {
HRESULT hResult = hrSuccess;
SCODE sc;
SRestriction res;
SRestriction resAnd[5]; // array for AND restrictions
SPropValue propObjectType, propEmail, propEmails, propDisplayName;
LPSPropValue lpPropArray = NULL;
LPSRowSet lpRow = NULL;
LPSRowSet lpSRowSet = NULL;
LPMAPITABLE lpTable = NULL;
LPMAPITABLE lpAmbiguousTable;
LPTABLEDATA FAR lpTableData = NULL;
LPADRENTRY lpAdrEntry;
LPMAPIPROP lpMailUser = NULL;
LPTSTR lpDisplayName = NULL, lpEmailAddress = NULL;
ULONG ulObjectType;
ULONG ulObjType, ulRowCount, i, j, index, cValues;
ULONG resCount;
BOOL bUnicode = ulFlags & WAB_RESOLVE_UNICODE;
Assert(lpAdrList->cEntries == lpFlagList->cFlags);
//
// Get the contents table for the PAB container
//
if (HR_FAILED(hResult = lpContainer->lpVtbl->GetContentsTable(lpContainer,
WAB_PROFILE_CONTENTS | WAB_CONTENTTABLE_NODATA | MAPI_UNICODE, // This table is internal to this function hence is in Unicode
&lpTable))) {
DebugTrace(TEXT("PAB GetContentsTable -> %x\n"), GetScode(hResult));
goto exit;
}
// Set the column set
if (HR_FAILED(hResult = lpTable->lpVtbl->SetColumns(lpTable,
(LPSPropTagArray)&ptaFind, 0))) {
DebugTrace(TEXT("PAB SetColumns-> %x\n"), GetScode(hResult));
goto exit;
}
// Set up the property values for restrictions
propObjectType.ulPropTag = PR_OBJECT_TYPE;
propEmail.ulPropTag = PR_EMAIL_ADDRESS;
propDisplayName.ulPropTag = PR_DISPLAY_NAME;
propEmails.ulPropTag = PR_CONTACT_EMAIL_ADDRESSES;
propEmails.Value.MVSZ.cValues = 1;
// All of our restrictions are AND restrictions using the resAnd array
res.rt = RES_AND;
res.res.resAnd.lpRes = resAnd;
// res.res.resAnd.cRes = 2; Caller must fill in before calling Restrict
//
// Loop through every entry, looking for those that need
// attention.
//
for (i = 0; i < lpFlagList->cFlags; i++)
{
lpAdrEntry = &lpAdrList->aEntries[i];
if (lpFlagList->ulFlag[i] == MAPI_UNRESOLVED ||
lpFlagList->ulFlag[i] == MAPI_AMBIGUOUS)
{
if(!bUnicode) // <note> assumes Unicode Defined
{
LocalFreeAndNull(&lpDisplayName);
LocalFreeAndNull(&lpEmailAddress);
}
else
lpDisplayName = lpEmailAddress = NULL; // init the strings
ulObjectType = 0; // invalid type
resCount = 0;
ulRowCount = 0;
// walk through the prop list for this entry looking for interesting props
for (j = 0; j < lpAdrEntry->cValues; j++)
{
ULONG ulPropTag = lpAdrEntry->rgPropVals[j].ulPropTag;
if(!bUnicode && PROP_TYPE(ulPropTag)==PT_STRING8) // <note> assumes Unicode Defined
ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_UNICODE);
if (ulPropTag == PR_OBJECT_TYPE)
{
ulObjectType = lpAdrEntry->rgPropVals[j].Value.l;
propObjectType.Value.ul = ulObjectType;
}
if (ulPropTag == PR_EMAIL_ADDRESS)
{
lpEmailAddress =(bUnicode) ?
lpAdrEntry->rgPropVals[j].Value.lpszW :
ConvertAtoW(lpAdrEntry->rgPropVals[j].Value.lpszA);
propEmails.Value.MVSZ.LPPSZ = &lpEmailAddress;
propEmail.Value.LPSZ = lpEmailAddress;
}
if (ulPropTag == PR_DISPLAY_NAME)
{
lpDisplayName = (bUnicode) ?
lpAdrEntry->rgPropVals[j].Value.lpszW :
ConvertAtoW(lpAdrEntry->rgPropVals[j].Value.lpszA);
propDisplayName.Value.LPSZ = lpDisplayName;
}
}
// Without email address, we can't improve on the standard resolve
if (lpEmailAddress)
{
// If unresolved, try PR_EMAIL_ADDRESS without displayname
if (lpFlagList->ulFlag[i] == MAPI_UNRESOLVED )
{
resCount = 0;
InitPropertyRestriction(&(resAnd[resCount++]), &propEmail);
if (ulObjectType)
{
InitPropertyRestriction(&(resAnd[resCount++]), &propObjectType);
}
if (ulFlags & WAB_RESOLVE_NEED_CERT)
{
resAnd[resCount].rt = RES_EXIST;
resAnd[resCount++].res.resExist.ulPropTag = PR_USER_X509_CERTIFICATE;
}
res.res.resAnd.cRes = resCount;
if (hResult = lpTable->lpVtbl->Restrict(lpTable, &res, 0))
{
goto exit;
}
// Did any match?
if (HR_FAILED(hResult = lpTable->lpVtbl->GetRowCount(lpTable, 0, &ulRowCount)))
{
DebugTrace(TEXT("GetRowCount from AB contents table -> %x\n"), GetScode(hResult));
goto exit;
}
switch (ulRowCount)
{
default: // too many
if (! (ulFlags & WAB_RESOLVE_FIRST_MATCH))
{
// No point in narrowing the search with PR_DISPLAY_NAME since
// we know it wasn't found. Also no point in putting in
// PR_CONTACT_EMAIL_ADDRESSES since that is ambiguous too.
// Drop out to the ambiguity handler
goto Ambiguity;
} // else fall through and take the first one
case 1: // Found one!
if (hResult = HrRowToADRENTRY(lpIAB, lpTable, lpAdrEntry, bUnicode))
{
goto exit;
}
lpFlagList->ulFlag[i] = MAPI_RESOLVED; // Mark this entry as found.
continue; // next entry
case 0:
// No match, try PR_CONTACT_EMAIL_ADDRESSES
// Create the restriction to find the email address in the multi-valued
// PR_CONTACT_EMAIL_ADDRESSES. (replace propEmail in the restriction)
resAnd[0].rt = RES_CONTENT;
resAnd[0].res.resContent.ulFuzzyLevel = FL_IGNORECASE | FL_FULLSTRING;
resAnd[0].res.resContent.ulPropTag = PR_CONTACT_EMAIL_ADDRESSES;
resAnd[0].res.resContent.lpProp = &propEmails;
if (hResult = lpTable->lpVtbl->Restrict(lpTable, &res, 0)) {
goto exit;
}
// Did any match?
if (HR_FAILED(hResult = lpTable->lpVtbl->GetRowCount(lpTable, 0, &ulRowCount))) {
DebugTrace(TEXT("GetRowCount from AB contents table -> %x\n"), GetScode(hResult));
goto exit;
}
switch (ulRowCount) {
default: // More than one. We'll catch it below.
// Drop out to the ambiguity handler
if (! (ulFlags & WAB_RESOLVE_FIRST_MATCH)) {
// No point in narrowing the search with PR_DISPLAY_NAME since
// we know it wasn't found.
// Drop out to the ambiguity handler
goto Ambiguity;
} // else fall through and take the first one
case 1: // Found one!
if (hResult = HrRowToADRENTRY(lpIAB, lpTable, lpAdrEntry, bUnicode)) {
goto exit;
}
lpFlagList->ulFlag[i] = MAPI_RESOLVED; // Mark this entry as found.
continue;
case 0:
// We're SOL on this one. Ignore it and move on.
continue;
}
break;
}
}
// We should only get here if there was a MAPI_AMBIGUOUS flag
//
// Look for PR_DISPLAY_NAME and PR_EMAIL_ADDRESS
//
// propEmail goes first so that we can replace it with propEmails later
InitPropertyRestriction(&(resAnd[resCount++]), &propEmail);
if (lpDisplayName) {
// Shouldn't add the display name in if it is the same as the email!
if (lstrcmpi(lpDisplayName, lpEmailAddress)) {
InitPropertyRestriction(&(resAnd[resCount++]), &propDisplayName);
}
}
if (ulObjectType) {
InitPropertyRestriction(&(resAnd[resCount++]), &propObjectType);
}
if (ulFlags & WAB_RESOLVE_NEED_CERT) {
resAnd[resCount].rt = RES_EXIST;
resAnd[resCount++].res.resExist.ulPropTag = PR_USER_X509_CERTIFICATE;
}
res.res.resAnd.cRes = resCount;
if (hResult = lpTable->lpVtbl->Restrict(lpTable, &res, 0)) {
goto exit;
}
// Did any match?
if (HR_FAILED(hResult = lpTable->lpVtbl->GetRowCount(lpTable, 0, &ulRowCount))) {
DebugTrace(TEXT("GetRowCount from AB contents table -> %x\n"), GetScode(hResult));
goto exit;
}
switch (ulRowCount) {
default: // too many
if (! (ulFlags & WAB_RESOLVE_FIRST_MATCH)) {
// No point in narrowing the search with PR_CONTACT_EMAIL_ADDRESSES
goto Ambiguity;
} // else fall through and take the first one
case 1: // Found one!
if (hResult = HrRowToADRENTRY(lpIAB, lpTable, lpAdrEntry, bUnicode)) {
goto exit;
}
lpFlagList->ulFlag[i] = MAPI_RESOLVED; // Mark this entry as found.
continue;
case 0:
// No match, try PR_DISPLAY_NAME and PR_CONTACT_EMAIL_ADDRESSES
// Create the restriction to find the email address in the multi-valued
// PR_CONTACT_EMAIL_ADDRESSES.
resAnd[0].rt = RES_CONTENT;
resAnd[0].res.resContent.ulFuzzyLevel = FL_IGNORECASE | FL_FULLSTRING;
resAnd[0].res.resContent.ulPropTag = PR_CONTACT_EMAIL_ADDRESSES;
resAnd[0].res.resContent.lpProp = &propEmails;
if (hResult = lpTable->lpVtbl->Restrict(lpTable, &res, 0)) {
goto exit;
}
// Did any match?
if (HR_FAILED(hResult = lpTable->lpVtbl->GetRowCount(lpTable, 0, &ulRowCount))) {
DebugTrace(TEXT("GetRowCount from AB contents table -> %x\n"), GetScode(hResult));
goto exit;
}
switch (ulRowCount) {
default: // More than one. We'll catch it below.
if (! (ulFlags & WAB_RESOLVE_FIRST_MATCH)) {
goto Ambiguity;
} // else fall through and take the first one
case 1: // Found one!
if (hResult = HrRowToADRENTRY(lpIAB, lpTable, lpAdrEntry, bUnicode)) {
goto exit;
}
lpFlagList->ulFlag[i] = MAPI_RESOLVED; // Mark this entry as found.
continue;
case 0:
// We're SOL on this one. Ignore it and move on.
continue;
}
break;
}
if (ulRowCount > 1)
{
Ambiguity:
// Ambiguous results still in table. We should do some more processing on
// this and if necesary, fill in the ambiguity table
// BUGBUG: Here is where we should add a restrict on the certificate property
if(lpAmbiguousTables)
{
// [PaulHi] 4/5/99 Use the Internal CreateTableData() function that takes
// the ulFlags and will deal with ANSI/UNICODE requests correctly
sc = CreateTableData(
NULL,
(ALLOCATEBUFFER FAR *) MAPIAllocateBuffer,
(ALLOCATEMORE FAR *) MAPIAllocateMore,
MAPIFreeBuffer,
NULL,
TBLTYPE_DYNAMIC,
PR_RECORD_KEY,
(LPSPropTagArray)&ITableColumns,
NULL,
0,
NULL,
ulFlags,
&lpTableData);
if ( FAILED(sc) )
{
DebugTrace(TEXT("CreateTableData() failed %x\n"), sc);
hResult = ResultFromScode(sc);
goto exit;
}
if(ulFlags & MAPI_UNICODE)
((TAD*)lpTableData)->bMAPIUnicodeTable = bUnicode;
// Allocate an SRowSet to hold the entries.
if (FAILED(sc = MAPIAllocateBuffer(sizeof(SRowSet) + ulRowCount* sizeof(SRow),
(LPVOID *)&lpSRowSet)))
{
DebugTrace(TEXT("Allocation of SRowSet failed\n"));
hResult = ResultFromScode(sc);
goto exit;
}
lpSRowSet->cRows = 0;
for (index = 0; index < ulRowCount; index++)
{
if (hResult = lpTable->lpVtbl->QueryRows(lpTable,1,0,&lpRow))
{
DebugTrace(TEXT("GetNextRowEID:QueryRows -> %x\n"), GetScode(hResult));
break;
}
else
{
// Found one, copy entryid to new allocation
if (!lpRow->cRows)
break;
if (HR_FAILED(hResult = lpIAB->lpVtbl->OpenEntry(lpIAB,
lpRow->aRow[0].lpProps[ifePR_ENTRYID].Value.bin.cb, // cbEntryID
(LPENTRYID)lpRow->aRow[0].lpProps[ifePR_ENTRYID].Value.bin.lpb, // entryid of first match
NULL, // interface
0, // ulFlags
&ulObjType, // returned object type
(LPUNKNOWN *)&lpMailUser)))
{
DebugTraceResult( TEXT("ResolveNames OpenEntry"), hResult);
goto exit;
}
Assert(lpMailUser);
FreeProws(lpRow);
lpRow = NULL;
// This is stuffed into the SRowSet, so don't free it
if (HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser,
(LPSPropTagArray)&ptaResolveDefaults, // lpPropTagArray
(bUnicode ? MAPI_UNICODE : 0), // ulFlags
&cValues, // how many properties were there?
&lpPropArray)))
{
DebugTraceResult( TEXT("ResolveNames GetProps"), hResult);
goto exit;
}
hResult = hrSuccess;
UlRelease(lpMailUser);
lpMailUser = NULL;
// [PaulHi] 2/1/99 GetProps will return UNICODE strings from the
// ptaResolveDefaults request array. Convert to ANSI if our client
// is not UNICODE.
if (!bUnicode)
{
// @review [PaulHi] I am fairly certain that this lpPropArray array
// will ALWAYS be allocated from our local MAPIAllocateMore function
// and never from ((LPIDAT)lpMailUser->lpPropData)->inst.lpfAllocateMore.
// Check this.
if(sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpPropArray, cValues, 0))
goto exit;
}
// Fixup the PR_RECORD_KEY
// Make certain we have proper indicies.
// For now, we will equate PR_INSTANCE_KEY and PR_RECORD_KEY to PR_ENTRYID.
lpPropArray[irdPR_INSTANCE_KEY].ulPropTag = PR_INSTANCE_KEY;
lpPropArray[irdPR_INSTANCE_KEY].Value.bin.cb =
lpPropArray[irdPR_ENTRYID].Value.bin.cb;
lpPropArray[irdPR_INSTANCE_KEY].Value.bin.lpb =
lpPropArray[irdPR_ENTRYID].Value.bin.lpb;
lpPropArray[irdPR_RECORD_KEY].ulPropTag = PR_RECORD_KEY;
lpPropArray[irdPR_RECORD_KEY].Value.bin.cb =
lpPropArray[irdPR_ENTRYID].Value.bin.cb;
lpPropArray[irdPR_RECORD_KEY].Value.bin.lpb =
lpPropArray[irdPR_ENTRYID].Value.bin.lpb;
// Put it in the RowSet
lpSRowSet->aRow[index].cValues = cValues; // number of properties
lpSRowSet->aRow[index].lpProps = lpPropArray; // LPSPropValue
}
}
// Add the rows to the table
lpSRowSet->cRows = index;
if (HR_FAILED(hResult = lpTableData->lpVtbl->HrModifyRows(lpTableData,
0,lpSRowSet)))
{
DebugTrace(TEXT("HrModifyRows for ambiguity table -> %x\n"), GetScode(hResult));
goto exit;
}
hResult = hrSuccess;
// Clean up the row set
FreeProws(lpSRowSet);
lpSRowSet = NULL;
if (lpTableData)
{
if (HR_FAILED(hResult = lpTableData->lpVtbl->HrGetView(lpTableData,
NULL, // LPSSortOrderSet lpsos,
ContentsViewGone, // CALLERRELEASE FAR * lpfReleaseCallback,
0, // ULONG ulReleaseData,
&lpAmbiguousTable)))
{
DebugTrace(TEXT("HrGetView of Ambiguity table -> %x\n"), ResultFromScode(hResult));
goto exit;
}
}
// Got a contents table; put it in the
// ambiguity tables list.
Assert(i < lpAmbiguousTables->cEntries);
lpAmbiguousTables->lpTable[i] = lpAmbiguousTable;
}
lpFlagList->ulFlag[i] = MAPI_AMBIGUOUS; // Mark this entry as ambiguous
}
}
}
}
exit:
if(!bUnicode) // <note> assumes Unicode Defined
{
LocalFreeAndNull(&lpDisplayName);
LocalFreeAndNull(&lpEmailAddress);
}
if (lpSRowSet) {
// Clean up the row set
FreeProws(lpSRowSet);
}
if (lpRow) {
FreeProws(lpRow);
}
UlRelease(lpMailUser);
UlRelease(lpTable);
if (hResult) {
DebugTrace(TEXT("HrSmartFind coudln't find %s %s <%s>\n"),
ulObjectType == MAPI_MAILUSER ? TEXT("Mail User") : TEXT("Distribution List"),
lpDisplayName ? lpDisplayName : TEXT(""),
lpEmailAddress ? lpEmailAddress : TEXT(""));
}
return(hResult);
}
/***************************************************************************
Name : CountFlags
Purpose : Count the ResolveNames flags in the FlagList.
Parameters: lpFlagList = flag list to count
lpulResolved -> return count of MAPI_RESOLVED here
lpulAmbiguous -> return count of MAPI_AMBIGUOUS here
lpulUnresolved -> return count of MAPI_UNRESOLVED here
Returns : none
Comment :
***************************************************************************/
void CountFlags(LPFlagList lpFlagList, LPULONG lpulResolved,
LPULONG lpulAmbiguous, LPULONG lpulUnresolved) {
register ULONG i;
*lpulResolved = *lpulAmbiguous = *lpulUnresolved = 0;
for (i = 0; i < lpFlagList->cFlags; i++) {
switch (lpFlagList->ulFlag[i]) {
case MAPI_AMBIGUOUS:
(*lpulAmbiguous)++;
break;
case MAPI_RESOLVED:
(*lpulResolved)++;
break;
case MAPI_UNRESOLVED:
(*lpulUnresolved)++;
break;
default:
Assert(lpFlagList->ulFlag[i]);
}
}
}
/***************************************************************************
Name : InitFlagList
Purpose : Initialize the flags in a FlagList based on values in the
matching ADRLIST
Parameters: lpFlagList = flag list to fill in
lpAdrList = adrlist to search
Returns : none
Comment : Initialize the flag to MAPI_RESOLVED if and only if the
corresponding ADRENTRY has a PR_ENTRYID that is non-NULL.
***************************************************************************/
void InitFlagList(LPFlagList lpFlagList, LPADRLIST lpAdrList) {
ULONG i, j;
LPADRENTRY lpAdrEntry;
Assert(lpAdrList->cEntries == lpFlagList->cFlags);
for (i = 0; i < lpFlagList->cFlags; i++) {
lpAdrEntry = &lpAdrList->aEntries[i];
lpFlagList->ulFlag[i] = MAPI_UNRESOLVED;
// No props? Then it's automatically resolved.
if (lpAdrEntry->cValues == 0) {
lpFlagList->ulFlag[i] = MAPI_RESOLVED;
}
// walk through the prop list for this user
for (j = 0; j < lpAdrEntry->cValues; j++) {
// Look for PR_ENTRYID which is not NULL
if (lpAdrEntry->rgPropVals[j].ulPropTag == PR_ENTRYID &&
lpAdrEntry->rgPropVals[j].Value.bin.cb != 0) {
// Already has a PR_ENTRYID, it's considered resolved.
lpFlagList->ulFlag[i] = MAPI_RESOLVED;
break;
}
}
}
}
/***************************************************************************
Name : UnresolveNoCerts
Purpose : Unresolve any entry in the ADRLIST which has no cert property
Parameters: lpIAB -> IAB object
lpFlagList = flag list to fill in
lpAdrList = adrlist to search
Returns : none
Comment : Initialize the flag to MAPI_RESOLVED if and only if the
corresponding ADRENTRY has a PR_ENTRYID that is non-NULL.
***************************************************************************/
HRESULT UnresolveNoCerts(LPIAB lpIAB, LPADRLIST lpAdrList, LPFlagList lpFlagList) {
HRESULT hr = hrSuccess;
register ULONG i;
LPADRENTRY lpAdrEntry;
ULONG ulObjType;
LPSPropValue lpspvEID, lpspvCERT, lpspvProp = NULL, lpspvNew = NULL;
ULONG cProps, cPropsNew;
LPMAILUSER lpMailUser = NULL;
SizedSPropTagArray(1, ptaCert) =
{ 1, {PR_USER_X509_CERTIFICATE} };
for (i = 0; i < lpFlagList->cFlags; i++) {
switch (lpFlagList->ulFlag[i]) {
case MAPI_RESOLVED:
// Look in the ADRENTRY for a PR_USER_X509_CERTIFICATE
lpAdrEntry = &lpAdrList->aEntries[i];
if (! (lpspvCERT = LpValFindProp(PR_USER_X509_CERTIFICATE,
lpAdrEntry->cValues, lpAdrEntry->rgPropVals))) {
// No property in the ADRLIST
// Does it exist on the underlying object?
if (! (lpspvEID = LpValFindProp(PR_ENTRYID,
lpAdrEntry->cValues, lpAdrEntry->rgPropVals))) {
// Weird!
Assert(FALSE);
// No cert prop, unmark it
lpFlagList->ulFlag[i] = MAPI_UNRESOLVED;
// Invalidate the entryid prop in the ADRENTRY
lpspvEID->Value.bin.cb = 0;
goto LoopContinue;
}
if (HR_FAILED(lpIAB->lpVtbl->OpenEntry(lpIAB,
lpspvEID->Value.bin.cb, // size of EntryID to open
(LPENTRYID)lpspvEID->Value.bin.lpb, // EntryID to open
NULL, // interface
0, // flags
&ulObjType,
(LPUNKNOWN *)&lpMailUser))) {
// No cert prop, unmark it
lpFlagList->ulFlag[i] = MAPI_UNRESOLVED;
// Invalidate the entryid prop in the ADRENTRY
lpspvEID->Value.bin.cb = 0;
goto LoopContinue;
} else {
if (lpMailUser) {
if (HR_FAILED(lpMailUser->lpVtbl->GetProps(lpMailUser,
(LPSPropTagArray)&ptaCert,
MAPI_UNICODE,
&cProps,
&lpspvProp))) {
// No cert prop, unmark it
lpFlagList->ulFlag[i] = MAPI_UNRESOLVED;
// Invalidate the entryid prop in the ADRENTRY
lpspvEID->Value.bin.cb = 0;
goto LoopContinue;
}
if (PROP_ERROR(lpspvProp[0])) {
// No cert prop, unmark it
lpFlagList->ulFlag[i] = MAPI_UNRESOLVED;
// Invalidate the entryid prop in the ADRENTRY
lpspvEID->Value.bin.cb = 0;
goto LoopContinue;
}
if (lpspvProp) {
// BUGBUG: Validate cert against our known e-mail address
// Parse the MVBin and do cert stuff. Yuck!
// Blow it off for now, assume they have the right one here.
// Put this cert in the ADRENTRY
// Merge the new props with the ADRENTRY props
if (ScMergePropValues(lpAdrEntry->cValues,
lpAdrEntry->rgPropVals, // source1
cProps,
lpspvProp, // source2
&cPropsNew,
&lpspvNew)) { // dest
// Can't merge cert prop, fail
lpFlagList->ulFlag[i] = MAPI_UNRESOLVED;
// Invalidate the entryid prop in the ADRENTRY
lpspvEID->Value.bin.cb = 0;
goto LoopContinue;
}
// Free old prop array and put new prop array in ADRENTRY
FreeBufferAndNull((LPVOID *) (&lpAdrEntry->rgPropVals));
lpAdrEntry->rgPropVals = lpspvNew;
lpAdrEntry->cValues = cPropsNew;
}
}
}
LoopContinue:
FreeBufferAndNull(&lpspvProp);
if (lpMailUser) {
lpMailUser->lpVtbl->Release(lpMailUser);
lpMailUser = NULL;
}
}
break;
case MAPI_AMBIGUOUS:
case MAPI_UNRESOLVED:
break;
default:
Assert(lpFlagList->ulFlag[i]);
}
}
return(hr);
}
/***************************************************************************
Name : ResolveLocal
Purpose : Resolve a name entered by a user against a local container
Parameters: lpIAB = This IAB object
cbEID = bytes in EntryID
lpEID = EntryID of container
lpAdrList = adrlist to search
lpFlagList = flag list to fill in
ulFlags = flags
lpAmbiguousTables = ambiguity dialog info
Returns : none
***************************************************************************/
void ResolveLocal(LPIAB lpIAB, ULONG cbEID, LPENTRYID lpEID,
LPADRLIST lpAdrList, LPFlagList lpFlagList, ULONG ulFlags,
LPAMBIGUOUS_TABLES lpAmbiguousTables)
{
HRESULT hr;
ULONG ulObjType, ulResolved, ulAmbiguous, ulUnresolved;
LPABCONT lpABCont = NULL;
hr = lpIAB->lpVtbl->OpenEntry(lpIAB, cbEID, lpEID, NULL, 0, &ulObjType, (LPUNKNOWN *)&lpABCont);
if (SUCCEEDED(hr))
{
ULONG Flags = 0;
if(bAreWABAPIProfileAware(lpIAB) &&
!(ulFlags & WAB_RESOLVE_USE_CURRENT_PROFILE))
Flags |= WAB_IGNORE_PROFILES;
if(ulFlags & WAB_RESOLVE_UNICODE)
Flags |= MAPI_UNICODE;
// Simple resolve on container - ignore errors
lpABCont->lpVtbl->ResolveNames( lpABCont, NULL,
Flags,
lpAdrList, lpFlagList);
// Make certain that any entries we found have a certificate property
// for this email address.
if (ulFlags & WAB_RESOLVE_NEED_CERT)
UnresolveNoCerts(lpIAB, lpAdrList, lpFlagList);
if (ulFlags & WAB_RESOLVE_ALL_EMAILS)
{
// If we need more aggressive resolution, use HrSmartResolve
// This is much slower, so use it judiciously.
// Count the flags
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
if (ulAmbiguous || ulUnresolved)
HrSmartResolve(lpIAB, lpABCont, ulFlags, lpAdrList, lpFlagList,
lpAmbiguousTables);
}
lpABCont->lpVtbl->Release(lpABCont);
}
}
/***************************************************************************
Name : ResolveCurrentProfile
Purpose : Resolve a name entered by a user against only the folders that
are listed in the current profile - this way a user doesnt get
unexpected name resolution results from what he sees in his
profile-enabled Outlook Express address book.
There is an assumption here that this function will only be called
in the very specific case that OE is running with profiles and the
user is pressing Ctrl-K to resolve names
In that case, we will target the contents of the users folders ..
if something resolves unambiguously good and fine, if something is
ambiguous we will mark it so, if it doesnt resolve ok ..
After we've hit the users folders for this resolution, we can then
call the regular ResolveLocal function to take care of the unmatched
entries ...
Parameters: lpIAB = This IAB object
lpAdrList = adrlist to search
lpFlagList = flag list to fill in
Returns : none
***************************************************************************/
HRESULT HrResolveCurrentProfile(LPIAB lpIAB, LPADRLIST lpAdrList, LPFlagList lpFlagList, BOOL bOutlook, BOOL bUnicode)
{
LPADRENTRY lpAdrEntry;
ULONG i, j;
ULONG ulCount = 1;
LPSBinary rgsbEntryIDs = NULL;
HRESULT hResult = hrSuccess;
LPSPropValue lpPropArrayNew = NULL,lpProps = NULL;
ULONG ulObjType = 0, cPropsNew = 0,ulcProps = 0;
SCODE sc = SUCCESS_SUCCESS;
ULONG ulProfileCount = 0;
LPSBinary lpsb = NULL;
ULONG iolkci, colkci;
OlkContInfo *rgolkci;
ULONG ulFlags = AB_FUZZY_FIND_ALL;
EnterCriticalSection(&lpIAB->cs);
#ifndef DONT_ADDREF_PROPSTORE
if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpIAB->lpPropertyStore))))
{
hResult = ResultFromScode(sc);
goto exitNotAddRefed;
}
#endif
if(!bOutlook)
ulFlags |= AB_FUZZY_FIND_PROFILEFOLDERONLY;
colkci = bOutlook ? lpIAB->lpPropertyStore->colkci : lpIAB->cwabci;
Assert(colkci);
rgolkci = bOutlook ? lpIAB->lpPropertyStore->rgolkci : lpIAB->rgwabci;
Assert(rgolkci);
// 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;
ulProfileCount = 0;
LocalFreeSBinary(lpsb);
lpsb = NULL;
lpAdrEntry = &(lpAdrList->aEntries[i]);
// Search for this address
for (j = 0; j < lpAdrEntry->cValues; j++)
{
ULONG ulPropTag = lpAdrEntry->rgPropVals[j].ulPropTag;
if(!bUnicode && PROP_TYPE(ulPropTag)==PT_STRING8) //<note> assumes UNICODE defined
ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_UNICODE);
if (ulPropTag == PR_DISPLAY_NAME || ulPropTag == PR_EMAIL_ADDRESS )
{
LPTSTR lpsz = (bUnicode) ?
lpAdrEntry->rgPropVals[j].Value.lpszW :
ConvertAtoW(lpAdrEntry->rgPropVals[j].Value.lpszA);
ulCount = 1; // number of matches to find
rgsbEntryIDs = NULL;
iolkci = bOutlook ? 0 : 1; // if it's outlook we DO want to search the first folder
// if it's WAB, and this is a profile-enabled session and we are only searching
// through the profile folders, then we shouldn't search through the shared contacts
// folder which is the first folder on the list ...
// Only if we find nothing in the user's folders should we look into the shared contact
// folder ..
while (iolkci < colkci && ulProfileCount<=1 )
{
if(ulCount && rgsbEntryIDs)
{
FreeEntryIDs(lpIAB->lpPropertyStore->hPropertyStore, ulCount, rgsbEntryIDs);
ulCount = 1;
rgsbEntryIDs = NULL;
}
// Search the property store
Assert(lpIAB->lpPropertyStore->hPropertyStore);
if (HR_FAILED(hResult = HrFindFuzzyRecordMatches(lpIAB->lpPropertyStore->hPropertyStore,
rgolkci[iolkci].lpEntryID,
lpsz,
ulFlags,
&ulCount,// IN: number of matches to find, OUT: number found
&rgsbEntryIDs)))
{
DebugTraceResult( TEXT("HrFindFuzzyRecordMatches"), hResult);
goto exit;
}
ulProfileCount += ulCount;
if(ulProfileCount > 1)
{
LocalFreeSBinary(lpsb);
lpsb = NULL;
}
if(ulCount == 1 && ulProfileCount == 1)
{
lpsb = LocalAlloc(LMEM_ZEROINIT, sizeof(SBinary));
if(lpsb)
{
lpsb->cb = rgsbEntryIDs[0].cb;
lpsb->lpb = LocalAlloc(LMEM_ZEROINIT, lpsb->cb);
if(lpsb->lpb)
CopyMemory(lpsb->lpb, rgsbEntryIDs[0].lpb, lpsb->cb);
}
}
// next container
iolkci++;
} //while loop
if(ulProfileCount == 0 && !bOutlook)
{
if(ulCount && rgsbEntryIDs)
{
FreeEntryIDs(lpIAB->lpPropertyStore->hPropertyStore, ulCount, rgsbEntryIDs);
ulCount = 1;
rgsbEntryIDs = NULL;
}
// Search the property store
Assert(lpIAB->lpPropertyStore->hPropertyStore);
if (HR_FAILED(hResult = HrFindFuzzyRecordMatches(lpIAB->lpPropertyStore->hPropertyStore,
rgolkci[0].lpEntryID,
lpsz,
ulFlags,
&ulCount,// IN: number of matches to find, OUT: number found
&rgsbEntryIDs)))
{
DebugTraceResult( TEXT("HrFindFuzzyRecordMatches"), hResult);
goto exit;
}
ulProfileCount += ulCount;
if(ulProfileCount > 1)
{
LocalFreeSBinary(lpsb);
lpsb = NULL;
}
if(ulCount == 1 && ulProfileCount == 1)
{
lpsb = LocalAlloc(LMEM_ZEROINIT, sizeof(SBinary));
if(lpsb)
{
lpsb->cb = rgsbEntryIDs[0].cb;
lpsb->lpb = LocalAlloc(LMEM_ZEROINIT, lpsb->cb);
if(lpsb->lpb)
CopyMemory(lpsb->lpb, rgsbEntryIDs[0].lpb, lpsb->cb);
}
}
} // if ulProfileCount..
// If after doing all the containers, we have only 1 item that resolved
if(ulProfileCount > 1)
{
// This is ambiguous within this profile so mark it ambiguous
DebugTrace(TEXT("ResolveNames found more than 1 match in Current Profile... MAPI_AMBIGUOUS\n"));
lpFlagList->ulFlag[i] = MAPI_AMBIGUOUS;
}
else if(ulProfileCount == 1)
{
if (!HR_FAILED(HrGetPropArray((LPADRBOOK)lpIAB, (LPSPropTagArray)&ptaResolveDefaults,
lpsb->cb, (LPENTRYID)lpsb->lpb,
(bUnicode ? MAPI_UNICODE : 0),
&ulcProps, &lpProps)))
{
// Merge the new props with the ADRENTRY props
if (sc = ScMergePropValues(lpAdrEntry->cValues,
lpAdrEntry->rgPropVals,
ulcProps,
lpProps,
&cPropsNew,
&lpPropArrayNew))
{
goto exit;
}
// Free the original prop value array
FreeBufferAndNull((LPVOID*) (&(lpAdrEntry->rgPropVals)));
lpAdrEntry->cValues = cPropsNew;
lpAdrEntry->rgPropVals = lpPropArrayNew;
FreeBufferAndNull(&lpProps);
// [PaulHi] Raid 66515
// We need to convert these properties to ANSI since we are now the
// UNICODE WAB and if our client is !MAPI_UNICODE
if (!bUnicode)
{
if(sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpPropArrayNew, cPropsNew, 0))
goto exit;
}
// Mark this entry as found.
lpFlagList->ulFlag[i] = MAPI_RESOLVED;
}
}
FreeEntryIDs(lpIAB->lpPropertyStore->hPropertyStore,
ulCount, rgsbEntryIDs);
rgsbEntryIDs = NULL;
break;
} // if PR_DISPLAY_NAME
}// for j ...
} // for i
exit:
#ifndef DONT_ADDREF_PROPSTORE
ReleasePropertyStore(lpIAB->lpPropertyStore);
exitNotAddRefed:
#endif
LeaveCriticalSection(&lpIAB->cs);
if(lpsb)
{
//Bug #101354 - (erici) Free leaked alloc.
LocalFreeSBinary(lpsb);
}
return(hResult);
}
/***************************************************************************
Name : IAB_ResolveName
Purpose : Resolve a name entered by a user
Parameters: lpIAB = This IAB object
ulUIParam = hwnd
ulFlags may contain MAPI_UNICODE or MAPI_DIALOG
If this is a profile based session, and we want to do
profile specific searches, then we must pass in
WAB_RESOLVE_USE_CURRENT_PROFILE. Failure to pass this in
will imply that the user wants to search the whole WAB.
lpszNewEntryTitle = title for ResolveName dialog
lpAdrList = ADRLIST input/output
Returns : HRESULT
Comment : For now, the search path is hard coded to be:
+ reply one-offs
+ WAB's default container
+ SMTP one-offs
+ LDAP containers
***************************************************************************/
STDMETHODIMP
IAB_ResolveName(LPIAB lpIAB,
ULONG_PTR ulUIParam,
ULONG ulFlags,
LPTSTR lpszNewEntryTitle,
LPADRLIST lpAdrList)
{
HRESULT hr = hrSuccess;
SCODE sc = S_OK;
LPFlagList lpFlagList = NULL;
LPAMBIGUOUS_TABLES lpAmbiguousTables = NULL;
ULONG ulUnresolved = 0;
ULONG ulResolved = 0;
ULONG ulAmbiguous = 0;
ULONG cbWABEID;
LPENTRYID lpWABEID = NULL;
ULONG ulObjType;
LPABCONT lpWABCont = NULL;
ULONG i;
LPPTGDATA lpPTGData = GetThreadStoragePointer();
#ifndef DONT_ADDREF_PROPSTORE
if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpIAB->lpPropertyStore)))) {
hr = ResultFromScode(sc);
goto exitNotAddRefed;
}
#endif
#ifdef PARAMETER_VALIDATION
// Make sure it's an IAB
//
if (BAD_STANDARD_OBJ(lpIAB, IAB_, ResolveName, lpVtbl)) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (ulUIParam && !IsWindow((HWND)ulUIParam)) {
DebugTraceArg(IAB_ResolveName, TEXT("Invalid window handle\n"));
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
// BUGBUG: What's this 512 and where does it come from?
if (lpszNewEntryTitle && IsBadStringPtr(lpszNewEntryTitle, 512)) {
DebugTraceArg(IAB_ResolveName, TEXT("lpszNewEntryTitle fails address check"));
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (lpAdrList && FBadAdrList(lpAdrList)) {
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
// Due to a flag mixup, WAB_RESOLVE_LOCAL_ONLY equals MAPI_UNICODE, therefore IAB_Resolve is the
// only function that does not take the MAPI_UNICODE flag but instead needs the special
// WAB_RESOLVE_UNICODE flag
//
//if (ulFlags & WAB_RESOLVE_UNICODE) {
// DebugTraceArg(IAB_ResolveName, TEXT("Invalid character width"));
// return(ResultFromScode(MAPI_E_BAD_CHARWIDTH));
//}
#endif // PARAMETER_VALIDATION
// Validate flags
if (ulFlags & ~(WAB_RESOLVE_UNICODE | MAPI_DIALOG | WAB_RESOLVE_LOCAL_ONLY |
WAB_RESOLVE_ALL_EMAILS | WAB_RESOLVE_NO_ONE_OFFS | WAB_RESOLVE_NEED_CERT |
WAB_RESOLVE_NO_NOT_FOUND_UI | WAB_RESOLVE_USE_CURRENT_PROFILE | WAB_RESOLVE_FIRST_MATCH)) {
// Unknown flags
DebugTraceArg(IAB_ResolveName, TEXT("Unknown flags"));
// return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
if ( (ulFlags & WAB_RESOLVE_NEED_CERT) && !(ulFlags & WAB_RESOLVE_NO_ONE_OFFS) ) {
DebugTrace(TEXT("ResolveName got WAB_RESOLVE_NEED_CERT without WAB_RESOLVE_NO_ONE_OFFS\n"));
// Assert(FALSE);
return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
/*
if ((ulFlags & WAB_RESOLVE_USE_CURRENT_PROFILE) && (ulFlags & WAB_RESOLVE_ALL_EMAILS)) {
DebugTrace(TEXT("ResolveName can't handle both WAB_RESOLVE_USE_CURRENT_PROFILE and WAB_RESOLVE_ALL_EMAILS\n"));
Assert(FALSE);
return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
*/
// A NULL address list is already resolved.
if (! lpAdrList) {
goto exit;
}
VerifyWABOpenExSession(lpIAB);
if (ulFlags & MAPI_DIALOG && ulUIParam) {
pt_hWndFind = (HWND) ulUIParam;
}
//
// Allocate the lpFlagList first and zero fill it.
if (sc = MAPIAllocateBuffer((UINT) CbNewSPropTagArray(lpAdrList->cEntries),
&lpFlagList)) {
hr = ResultFromScode(sc);
goto exit;
}
MAPISetBufferName(lpFlagList, TEXT("WAB: lpFlagList in IAB_ResolveName"));
lpFlagList->cFlags = lpAdrList->cEntries;
InitFlagList(lpFlagList, lpAdrList);
// Count the flags
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
// Allocate the Ambiguous Table list and zero fill it.
if (sc = MAPIAllocateBuffer(sizeof(AMBIGUOUS_TABLES) + lpAdrList->cEntries * sizeof(LPMAPITABLE),
(LPVOID*)&lpAmbiguousTables)) {
hr = ResultFromScode(sc);
goto exit;
}
MAPISetBufferName(lpAmbiguousTables, TEXT("IAB_ResolveNames:AmbiguousTables"));
lpAmbiguousTables->cEntries = lpAdrList->cEntries;
for (i = 0; i < lpAmbiguousTables->cEntries; i++) {
lpAmbiguousTables->lpTable[i] = NULL;
}
if (! (ulFlags & WAB_RESOLVE_NO_ONE_OFFS) && (ulAmbiguous || ulUnresolved)) {
// Resolve any PR_DISPLAY_NAME:PR_EMAIL_ADDRESS pairs to one-offs.
HrResolveOneOffs(lpIAB, lpAdrList, lpFlagList,
(ulFlags & WAB_RESOLVE_UNICODE)?MAPI_UNICODE:0,
RECEIVED_EMAIL_ADDRESS);
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
}
if( bAreWABAPIProfileAware(lpIAB) && bIsThereACurrentUser(lpIAB) &&
ulFlags&WAB_RESOLVE_USE_CURRENT_PROFILE && (ulAmbiguous || ulUnresolved))
{
HrResolveCurrentProfile(lpIAB, lpAdrList, lpFlagList, FALSE, (ulFlags & WAB_RESOLVE_UNICODE));
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
}
// Do the default WAB container
if ((ulAmbiguous || ulUnresolved) &&
!(ulFlags & WAB_RESOLVE_USE_CURRENT_PROFILE))
{
if (! (hr = lpIAB->lpVtbl->GetPAB(lpIAB,&cbWABEID,&lpWABEID)))
{
ResolveLocal(lpIAB, cbWABEID, lpWABEID, lpAdrList, lpFlagList, ulFlags, lpAmbiguousTables);
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
FreeBufferAndNull(&lpWABEID);
}
}
// Do the additional containers
if (pt_bIsWABOpenExSession)
{
HrResolveCurrentProfile(lpIAB, lpAdrList, lpFlagList, TRUE, (ulFlags & WAB_RESOLVE_UNICODE));
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
}
if (! (ulFlags & WAB_RESOLVE_NO_ONE_OFFS)) {
if (ulUnresolved) {
//
// Take care of any Internet one-off's
//
if (ulUnresolved) {
hr = HrResolveOneOffs(lpIAB, lpAdrList, lpFlagList,
(ulFlags & WAB_RESOLVE_UNICODE)?MAPI_UNICODE:0,
ENTERED_EMAIL_ADDRESS);
}
// Count the flags
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
}
if (ulAmbiguous)
{
// Resolve any valid e-mail addresses that are ambiguous
hr = HrResolveOneOffs(lpIAB, lpAdrList, lpFlagList,
(ulFlags & WAB_RESOLVE_UNICODE)?MAPI_UNICODE:0,
AMBIGUOUS_EMAIL_ADDRESS);
// Count the flags
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
}
}
//
// Search any LDAP containers
//
if (! (ulFlags & WAB_RESOLVE_LOCAL_ONLY) && ulUnresolved)
{
if (! (hr = LDAPResolveName((LPADRBOOK)lpIAB, lpAdrList, lpFlagList, lpAmbiguousTables, ulFlags)))
{
if (ulFlags & WAB_RESOLVE_NEED_CERT)
{
// Make certain that any entries we found have a certificate property
// for this email address.
UnresolveNoCerts(lpIAB, lpAdrList, lpFlagList);
}
// Count the flags
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
}
}
// If caller just wants the first match, pull them out of the ambiguity tables
if (ulFlags & WAB_RESOLVE_FIRST_MATCH && ulAmbiguous)
{
Assert(lpAdrList->cEntries == lpAmbiguousTables->cEntries);
for (i = 0; i < lpAmbiguousTables->cEntries; i++)
{
if (lpAmbiguousTables->lpTable[i])
{
LPADRENTRY lpAdrEntry = &lpAdrList->aEntries[i];
// Get the first row from this table and return it.
if (SUCCEEDED(HrRowToADRENTRY(lpIAB, lpAmbiguousTables->lpTable[i], lpAdrEntry, (ulFlags & WAB_RESOLVE_UNICODE))))
{
lpFlagList->ulFlag[i] = MAPI_RESOLVED; // Mark this entry as found.
UlRelease(lpAmbiguousTables->lpTable[i]);
lpAmbiguousTables->lpTable[i] = NULL;
}
}
}
// Count the flags
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
}
// Do UI if needed
if ((ulFlags & MAPI_DIALOG) && (ulAmbiguous || ulUnresolved))
{
#ifdef OLD_STUFF
// Dump the ambiguous tables
for (i = 0; i < lpAmbiguousTables->cEntries; i++) {
if (lpAmbiguousTables->lpTable[i]) {
DebugMapiTable(lpAmbiguousTables->lpTable[i]);
}
}
#endif // OLD_STUFF
// Do the UI here.
hr = HrShowResolveUI((LPADRBOOK)lpIAB, (HWND) ulUIParam,
lpIAB->lpPropertyStore->hPropertyStore,
ulFlags,
&lpAdrList, &lpFlagList, lpAmbiguousTables);
if (ulFlags & WAB_RESOLVE_NEED_CERT)
{
// Make certain that any entries we found have a certificate property
// for this email address.
UnresolveNoCerts(lpIAB, lpAdrList, lpFlagList);
}
// Count the flags
CountFlags(lpFlagList, &ulResolved, &ulAmbiguous, &ulUnresolved);
}
if (! hr)
{
if (ulAmbiguous)
hr = ResultFromScode(MAPI_E_AMBIGUOUS_RECIP);
else if (ulUnresolved)
hr = ResultFromScode(MAPI_E_NOT_FOUND);
}
exit:
#ifndef DONT_ADDREF_PROPSTORE
ReleasePropertyStore(lpIAB->lpPropertyStore);
exitNotAddRefed:
#endif
if (lpAmbiguousTables) {
for (i = 0; i < lpAmbiguousTables->cEntries; i++) {
UlRelease(lpAmbiguousTables->lpTable[i]);
}
FreeBufferAndNull(&lpAmbiguousTables);
}
FreeBufferAndNull(&lpFlagList);
if(ulFlags&MAPI_DIALOG && ulUIParam)
{
LPPTGDATA lpPTGData=GetThreadStoragePointer();
pt_hWndFind = NULL;
}
return(hr);
}
/***************************************************************************
Name : IsDomainName
Purpose : Is this domain correctly formatted for an Internet address?
Parameters: lpDomain -> Domain name to check
fEnclosure = TRUE if the address started with '<'.
fTrimEnclosure = TRUE if we should truncate the address to
remove the '>'.
Returns : TRUE if the domain is a correct format for an Internet
address.
Comment : Valid domain names have this form:
bar[.bar]*
where bar must have non-empty contents
no high bits are allowed on any characters
no '@' allowed
the '>' character ends the address if there was a '<'
character at the start of the address.
***************************************************************************/
BOOL IsDomainName(LPTSTR lpDomain, BOOL fEnclosure, BOOL fTrimEnclosure) {
if (lpDomain) {
if (*lpDomain == '\0' || *lpDomain == '.' || (fEnclosure && *lpDomain == '>')) {
// domain name must have contents and can't start with '.'
return(FALSE);
}
while (*lpDomain && (! fEnclosure || *lpDomain != '>')) {
// Internet addresses only allow pure ASCII. No high bits!
// No more '@' or '<' characters allowed.
if (*lpDomain >= 0x0080 || *lpDomain == '@' || *lpDomain == '<') {
return(FALSE);
}
if (*lpDomain == '.') {
// Recursively check this part of the domain name
return(IsDomainName(CharNext(lpDomain), fEnclosure, fTrimEnclosure));
}
lpDomain = CharNext(lpDomain);
}
if (fEnclosure) {
if (*lpDomain != '>') {
return(FALSE);
}
// Must be the last thing done before returning TRUE!
if (fTrimEnclosure && *lpDomain == '>') {
*lpDomain = '\0';
}
}
return(TRUE);
}
return(FALSE);
}
/***************************************************************************
Name : IsInternetAddress
Purpose : Is this address correctly formatted for an Internet address?
Parameters: lpAddress -> Address to check
lppEmail -> Returned email address (May be NULL input if
email should not be parsed.)
Returns : TRUE if the address is a correct format for an Internet
address.
Comment : Valid addresses have this form:
[display name <]foo@bar[.bar]*[>]
where foo and bar must have non-empty contents
and if there is a display name, there must be angle
brackets surrounding the email address.
***************************************************************************/
BOOL IsInternetAddress(LPTSTR lpAddress, LPTSTR * lppEmail) {
if (lpAddress) {
BOOL fEnclosure = FALSE;
LPTSTR lpDisplay = lpAddress;
LPTSTR lpTemp = lpAddress;
LPTSTR lpBracket = NULL;
// Get past any DisplayName stuff
for(lpTemp = lpAddress; *lpTemp && *lpTemp != '<'; lpTemp = CharNext(lpTemp)); // Looking for NULL or '<'
if (*lpTemp) {
Assert(*lpTemp == '<');
// Found an enclosure.
// if we are returning the email, plop down a NULL at the end of the display name
lpBracket = lpTemp;
// Get past the '<' to the SMTP email address
lpTemp++;
fEnclosure = TRUE;
lpAddress = lpTemp;
} else {
lpTemp = lpAddress;
}
// Can't start with '@'
if (*lpTemp == '@') {
return(FALSE);
}
// Step through the address looking for '@'. If there's an at sign in the middle
// of a string, this is close enough to being an internet address for me.
while (*lpTemp) {
// Internet addresses only allow pure ASCII. No high bits!
WCHAR wc = *lpTemp;
if(wc > 0x007f)
return FALSE;
//if (*lpTemp & 0x80)
//{
// return(FALSE);
//}
if (*lpTemp == '@') {
// Found the at sign. Is there anything following?
// (Must NOT be another '@')
if (IsDomainName(CharNext(lpTemp), fEnclosure, !!lppEmail)) {
if (lppEmail) { // Want to parse into Display & Email
if (lpBracket) { // Seperate Display & Email
*lpBracket = '\0';
// Trim the trailing spaces from the display name
TrimSpaces(lpDisplay);
}
*lppEmail = lpAddress;
}
return(TRUE);
} else {
return(FALSE);
}
}
lpTemp = CharNext(lpTemp);
}
}
return(FALSE);
}
/***************************************************************************
Name : ScNewOOEID
Purpose : AllocateMore a One-Off EntryID
Parameters: lpsbin -> returned SBinary EntryID
lpRoot = buffer to allocateMore onto
szDisplayName = display name
szAddress = email address (may be == szDisplayName)
szAddrType = addrtype
bIsUnicode -> TRUE if caller expects Unicode MAPI EID strings
Returns : SCODE
Comment :
***************************************************************************/
SCODE ScNewOOEID(
LPSBinary lpsbin,
LPVOID lpRoot,
LPTSTR szDisplayName,
LPTSTR szAddress,
LPTSTR szAddrType,
BOOL bIsUnicode)
{
return(GetScode(CreateWABEntryIDEx(bIsUnicode, WAB_ONEOFF, (LPVOID) szDisplayName, (LPVOID) szAddrType, (LPVOID) szAddress, 0, 0,
(LPVOID) lpRoot, (LPULONG) (&lpsbin->cb), (LPENTRYID *)&lpsbin->lpb)));
}
/***************************************************************************
Name : HrResolveOneOffs
Purpose : Resolves any Internet addresses in the ADRLIST.
Parameters: lpIAB -> IAddrBook object
lpAdrList -> input/output ADRLIST as with Resolvenames
lpFlagList -> flag list as with ResolveNames
ResolveType = type of one-off resolve to do
ulFlags - 0 or MAPI_UNICODE (if 0 means all strings in AdrList are ANSI/DBCS)
Returns : HRESULT
Comment :
***************************************************************************/
enum {
ioopPR_DISPLAY_NAME = 0,
ioopPR_EMAIL_ADDRESS,
ioopPR_ADDRTYPE,
ioopPR_ENTRYID,
ioopPR_OBJECT_TYPE,
ioopMAX
};
const TCHAR szSMTP[] = TEXT("SMTP");
#define CB_SMTP sizeof(szSMTP)
HRESULT HrResolveOneOffs(LPIAB lpIAB, LPADRLIST lpAdrList, LPFlagList lpFlagList,
ULONG ulFlags,
RESOLVE_TYPE ResolveType)
{
HRESULT hResult = hrSuccess;
SCODE sc = SUCCESS_SUCCESS;
ULONG i, j, k;
LPADRENTRY lpAdrEntry = NULL;
LPSPropValue lpPropArrayTemp = NULL, lpPropArrayNew = NULL;
LPTSTR lpszDisplayName = NULL, lpszEmailAddress = NULL;
ULONG cbTemp, cbEmailAddress, cPropsNew;
LPBYTE lpb;
BOOL fNotDone;
// Walk through the flag list, looking for unresolved entries:
for (i = 0; i < lpFlagList->cFlags; i++)
{
BOOL bAmbiguous = FALSE;
if(ResolveType == AMBIGUOUS_EMAIL_ADDRESS && lpFlagList->ulFlag[i] == MAPI_AMBIGUOUS)
{
// Fake the routine into thinking this is an unresolved internet address
bAmbiguous = TRUE;
lpFlagList->ulFlag[i] = MAPI_UNRESOLVED;
}
if (lpFlagList->ulFlag[i] == MAPI_UNRESOLVED)
{
// Found an unresolved entry. Look at the PR_DISPLAY_NAME
// and, if RECEIVED_EMAIL_ADDRESS, PR_EMAIL_ADDRESS.
lpAdrEntry = &(lpAdrList->aEntries[i]);
if(ulFlags & MAPI_UNICODE)
{
lpszDisplayName = NULL;
lpszEmailAddress = NULL;
}
else
{
// [PaulHi] 12/17/98 Raid #62242
// Don't deallocate twice if the two pointers are equal.
if (lpszEmailAddress != lpszDisplayName)
LocalFreeAndNull(&lpszEmailAddress);
LocalFreeAndNull(&lpszDisplayName);
lpszEmailAddress = NULL;
}
fNotDone = TRUE;
for (j = 0; j < lpAdrEntry->cValues && fNotDone; j++)
{
ULONG ulPropTag = lpAdrEntry->rgPropVals[j].ulPropTag;
if(!(ulFlags & MAPI_UNICODE) && PROP_TYPE(ulPropTag)==PT_STRING8)
ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_UNICODE);
switch (ulPropTag)
{
case PR_DISPLAY_NAME:
lpszDisplayName = (ulFlags & MAPI_UNICODE) ?
lpAdrEntry->rgPropVals[j].Value.lpszW :
ConvertAtoW(lpAdrEntry->rgPropVals[j].Value.lpszA);
switch (ResolveType)
{
case AMBIGUOUS_EMAIL_ADDRESS:
case ENTERED_EMAIL_ADDRESS:
// Just look at the display name
// we'll check it's validity as an email address.
break;
case RECEIVED_EMAIL_ADDRESS:
// If we are in RECEIVED_EMAIL_ADDRESS mode, find the email address.
// if it isn't there, this address doesn't resolve.
//
if (! lpszEmailAddress)
{
// Haven't seen it yet, go hunt for it.
for (k = j + 1; k < lpAdrEntry->cValues; k++)
{
ULONG ulPropTag = lpAdrEntry->rgPropVals[k].ulPropTag;
if(!(ulFlags & MAPI_UNICODE) && PROP_TYPE(ulPropTag)==PT_STRING8)
ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_UNICODE);
if (ulPropTag == PR_EMAIL_ADDRESS)
{
lpszEmailAddress = (ulFlags & MAPI_UNICODE) ?
lpAdrEntry->rgPropVals[k].Value.lpszW :
ConvertAtoW(lpAdrEntry->rgPropVals[k].Value.lpszA);
break; // out of for loop
}
}
if (! lpszEmailAddress)
{
// No email address, can't resolve in
// RECEIVED_EMAIL_ADDRESS mode.
fNotDone = FALSE; // exit this ADRENTRY
continue; // break binds to switch, not for.
}
}
break; // found email addr and display name. It's a one-off.
default:
Assert(FALSE);
}
// At this point, we have two pointers: lpszDisplayName and maybe lpszEmailAddress.
// Is it an Internet address or a RECEIVED_EMAIL_ADDRESS?
if ((ResolveType == RECEIVED_EMAIL_ADDRESS && lpszEmailAddress
&& lpszDisplayName)
|| IsInternetAddress(lpszDisplayName, &lpszEmailAddress))
{
if (lpszEmailAddress)
{
// We can resolve this.
cbEmailAddress = sizeof(TCHAR)*(lstrlen(lpszEmailAddress) + 1);
// Allocate a temporary prop array for our new properties
cbTemp = ioopMAX * sizeof(SPropValue) + cbEmailAddress + CB_SMTP;
if (sc = MAPIAllocateBuffer(cbTemp, &lpPropArrayTemp))
{
goto exit;
}
MAPISetBufferName(lpPropArrayTemp, TEXT("WAB: lpPropArrayTemp in HrResolveOneOffs"));
lpb = (LPBYTE)&lpPropArrayTemp[ioopMAX]; // point past array
if(!lstrlen(lpszDisplayName))
lpszDisplayName = lpszEmailAddress;
else if(*lpszDisplayName == '"')
{
// strip out the leading quote if it's the only one ..
LPTSTR lp = lpszDisplayName;
int nQuoteCount = 0;
while(lp && *lp)
{
if(*lp == '"')
nQuoteCount++;
lp = CharNext(lp);
}
if(nQuoteCount == 1)
StrCpyN(lpszDisplayName, lpszDisplayName+1, lstrlen(lpszDisplayName)+1);
}
{
LPTSTR lp = NULL;
DWORD cchSize = (lstrlen(lpszDisplayName)+1);
if(sc = MAPIAllocateMore(sizeof(TCHAR)*cchSize, lpPropArrayTemp, &lp))
goto exit;
StrCpyN(lp, lpszDisplayName, cchSize);
lpPropArrayTemp[ioopPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME;
lpPropArrayTemp[ioopPR_DISPLAY_NAME].Value.LPSZ = lp;
}
// Fill in our temp prop array
lpPropArrayTemp[ioopPR_EMAIL_ADDRESS].ulPropTag = PR_EMAIL_ADDRESS;
lpPropArrayTemp[ioopPR_EMAIL_ADDRESS].Value.LPSZ = (LPTSTR)lpb;
StrCpyN((LPTSTR)lpb, lpszEmailAddress, cbEmailAddress/sizeof(TCHAR));
lpb += cbEmailAddress;
lpPropArrayTemp[ioopPR_ADDRTYPE].ulPropTag = PR_ADDRTYPE;
lpPropArrayTemp[ioopPR_ADDRTYPE].Value.LPSZ = (LPTSTR)lpb;
StrCpyN((LPTSTR)lpb, szSMTP, CB_SMTP / sizeof(TCHAR));
lpb += CB_SMTP;
lpPropArrayTemp[ioopPR_ENTRYID].ulPropTag = PR_ENTRYID;
if (sc = ScNewOOEID(&lpPropArrayTemp[ioopPR_ENTRYID].Value.bin,
lpPropArrayTemp, // allocate more on here
lpszDisplayName,
lpszEmailAddress,
(LPTSTR)szSMTP,
(ulFlags & MAPI_UNICODE)))
{
goto exit;
}
lpPropArrayTemp[ioopPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE;
lpPropArrayTemp[ioopPR_OBJECT_TYPE].Value.l = MAPI_MAILUSER;
if(!(ulFlags & MAPI_UNICODE))
{
if (sc = ScConvertWPropsToA((LPALLOCATEMORE) (&MAPIAllocateMore), lpPropArrayTemp, ioopMAX, 0))
goto exit;
}
if (sc = ScMergePropValues(lpAdrEntry->cValues,
lpAdrEntry->rgPropVals, // source1
ioopMAX,
lpPropArrayTemp, // source2
&cPropsNew,
&lpPropArrayNew))
{
goto exit;
}
FreeBufferAndNull(&lpPropArrayTemp);
// Free the original prop value array
FreeBufferAndNull((LPVOID *) (&(lpAdrEntry->rgPropVals)));
// Now, build the new ADRENTRY
lpAdrEntry->cValues = cPropsNew;
lpAdrEntry->rgPropVals = lpPropArrayNew;
// Mark this entry as found.
lpFlagList->ulFlag[i] = MAPI_RESOLVED;
}
}
// Once we've found PR_DISPLAY_NAME we don't need to look at
// any more props. Jump to next ADRENTRY.
fNotDone = FALSE; // exit this ADRENTRY
continue;
case PR_EMAIL_ADDRESS:
lpszEmailAddress = (ulFlags & MAPI_UNICODE) ?
lpAdrEntry->rgPropVals[j].Value.lpszW :
ConvertAtoW(lpAdrEntry->rgPropVals[j].Value.lpszA);
break;
}
}
}
// if the ambiguity could not be resolved as an email, reset it
if(bAmbiguous && lpFlagList->ulFlag[i] == MAPI_UNRESOLVED)
lpFlagList->ulFlag[i] = MAPI_AMBIGUOUS;
}
exit:
hResult = ResultFromScode(sc);
if(!(ulFlags & MAPI_UNICODE))
{
if(lpszEmailAddress != lpszDisplayName)
LocalFreeAndNull(&lpszEmailAddress);
LocalFreeAndNull(&lpszDisplayName);
}
return(hResult);
}
//---------------------------------------------------------------------------
// Name: IAB_NewEntry()
//
// Description:
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
IAB_NewEntry(LPIAB lpIAB,
ULONG_PTR ulUIParam,
ULONG ulFlags,
ULONG cbEIDContainer,
LPENTRYID lpEIDContainer,
ULONG cbEIDNewEntryTpl,
LPENTRYID lpEIDNewEntryTpl,
ULONG FAR * lpcbEIDNewEntry,
LPENTRYID FAR * lppEIDNewEntry)
{
HRESULT hr = hrSuccess;
BOOL bChangesMade = FALSE;
BYTE bType;
SCODE sc;
#ifndef DONT_ADDREF_PROPSTORE
if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpIAB->lpPropertyStore)))) {
hr = ResultFromScode(sc);
goto exitNotAddRefed;
}
#endif
// BUGBUG <JasonSo>: This code does not handle the Container param at all.
VerifyWABOpenExSession(lpIAB);
bType = IsWABEntryID(cbEIDNewEntryTpl, lpEIDNewEntryTpl, NULL, NULL, NULL, NULL, NULL);
if (bType == WAB_DEF_MAILUSER || cbEIDNewEntryTpl == 0)
{
if(!lpcbEIDNewEntry || !lppEIDNewEntry)
{
hr = MAPI_E_INVALID_PARAMETER;
goto exit;
}
*lpcbEIDNewEntry = 0;
*lppEIDNewEntry = NULL;
hr = HrShowDetails( (LPADRBOOK)lpIAB,
(HWND) ulUIParam,
lpIAB->lpPropertyStore->hPropertyStore,
cbEIDContainer,
lpEIDContainer,
lpcbEIDNewEntry,
lppEIDNewEntry,
NULL,
SHOW_NEW_ENTRY,
MAPI_MAILUSER,
&bChangesMade);
}
else if (bType == WAB_DEF_DL)
{
hr = HrShowDetails( (LPADRBOOK)lpIAB,
(HWND) ulUIParam,
lpIAB->lpPropertyStore->hPropertyStore,
cbEIDContainer,
lpEIDContainer,
lpcbEIDNewEntry,
lppEIDNewEntry,
NULL,
SHOW_NEW_ENTRY,
MAPI_DISTLIST,
&bChangesMade);
}
else
{
DebugTrace(TEXT("IAB_NewEntry got unknown template entryID\n"));
hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
goto exit;
}
exit:
#ifndef DONT_ADDREF_PROPSTORE
ReleasePropertyStore(lpIAB->lpPropertyStore);
exitNotAddRefed:
#endif
return(hr);
}
//---------------------------------------------------------------------------
// Name: IAB_Address()
//
// Description:
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
IAB_Address(LPIAB lpIAB,
ULONG_PTR FAR * lpulUIParam,
LPADRPARM lpAdrParms,
LPADRLIST FAR * lppAdrList)
{
SCODE sc;
HRESULT hr = hrSuccess;
// OOPENTRYIDCONT oopEntryID;
// LPMAPIERROR lpMAPIError = NULL;
// MAPIDLG_Address FAR *lpfnAddress;
// BOOL fInited;
#ifndef DONT_ADDREF_PROPSTORE
if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpIAB->lpPropertyStore)))) {
hr = ResultFromScode(sc);
goto exitNotAddRefed;
}
#endif
#ifdef PARAMETER_VALIDATION
// Make sure it's an IAB
//
if (BAD_STANDARD_OBJ(lpIAB, IAB_, Address, lpVtbl))
{
DebugTraceArg(IAB_Address, TEXT("Bad vtable"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Validate Parameters
//
if ((lpulUIParam && IsBadWritePtr(lpulUIParam, sizeof(ULONG)))
|| (!lpAdrParms || IsBadWritePtr(lpAdrParms, sizeof(ADRPARM))))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpulUIParam or lpAdrParms"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Validate AdrParm
//
// validate lpAdrParm->cbABContEntryID and lpAdrParm->lpABContEntryID
if (lpAdrParms->cbABContEntryID
&& (!lpAdrParms->lpABContEntryID || IsBadReadPtr(lpAdrParms->lpABContEntryID,
(UINT)lpAdrParms->cbABContEntryID)))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParam->lpABContEntryID"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// validate lpAdrParm->lpfnABSDI, only used if DIALOG_SDI is set.
if (lpAdrParms->ulFlags & DIALOG_SDI)
{
if (lpAdrParms->lpfnABSDI && IsBadCodePtr((FARPROC)lpAdrParms->lpfnABSDI))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParam->lpfnABSDI"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
}
//
// Validate lpAdrList, if the call would allow modification of the list
//
if (lpAdrParms->ulFlags & DIALOG_MODAL)
{
if (lppAdrList) // Treat NULL as a special case of don't care
{
if (IsBadWritePtr(lppAdrList, sizeof(LPADRLIST)))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lppAdrList"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (*lppAdrList && FBadAdrList(*lppAdrList))
{
DebugTraceArg(IAB_Address, TEXT("Invalid *lppAdrList"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
}
}
//
// Check strings
//
//
// lpszCaption - goes on the top of the dialog on the caption bar
//
if (lpAdrParms->lpszCaption
&& (lpAdrParms->ulFlags & MAPI_UNICODE
? IsBadStringPtrW((LPWSTR) lpAdrParms->lpszCaption, (UINT) -1)
: IsBadStringPtrA((LPSTR) lpAdrParms->lpszCaption, (UINT) -1)))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->lpszCaption"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
//
// lpszNewEntryTitle - Goes on the NewEntry dialog by the radio button, uninteresting if
// AB_SELECTONLY is set.
//
if (!(lpAdrParms->ulFlags & AB_SELECTONLY) && lpAdrParms->lpszNewEntryTitle
&& (lpAdrParms->ulFlags & MAPI_UNICODE
? IsBadStringPtrW((LPWSTR) lpAdrParms->lpszNewEntryTitle, (UINT) -1)
: IsBadStringPtrA((LPSTR) lpAdrParms->lpszNewEntryTitle, (UINT) -1)))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->lpszNewEntryTitle"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
//
// Only check the following parameters if cDestFields is non-zero and !-1
//
if (lpAdrParms->cDestFields && lpAdrParms->cDestFields != (ULONG) -1)
{
ULONG ulString;
//
// lpszDestWellsTitle - Goes above the destination wells, uninteresting if 0 wells is
// brought up.
//
if (lpAdrParms->lpszNewEntryTitle
&& (lpAdrParms->ulFlags & MAPI_UNICODE
? IsBadStringPtrW((LPWSTR) lpAdrParms->lpszNewEntryTitle, (UINT) -1)
: IsBadStringPtrA((LPSTR) lpAdrParms->lpszNewEntryTitle, (UINT) -1)))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->lpszNewEntryTitle"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
//
// nDestFieldFocus - needs to be less than cDestFields unless cDestFields is 0.
//
if (lpAdrParms->nDestFieldFocus >= lpAdrParms->cDestFields)
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->nDestFieldFocus"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
//
// lppszDestTitles - should be more like rglpszDestTitles[cDestFields]. Each string
// should be valid (i.e. not NULL although "" is acceptable).
//
if (lpAdrParms->lppszDestTitles)
{
//
// Loop through each title and see if there's a valid string
//
for (ulString = 0; ulString < lpAdrParms->cDestFields; ulString++)
{
if (!*(lpAdrParms->lppszDestTitles+ulString)
|| (lpAdrParms->ulFlags & MAPI_UNICODE
? IsBadStringPtrW((LPWSTR) *(lpAdrParms->lppszDestTitles+ulString), (UINT)-1)
: IsBadStringPtrA((LPSTR) *(lpAdrParms->lppszDestTitles+ulString), (UINT)-1)))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->lppszDestTitles"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
}
}
//
// lpulDestComps - should be more like rgulDestComps[cDestFields]. This is the value
// for the PR_RECIPIENT_TYPE for messages. In fact, on the adrlist that is returned from
// this method, one of the list of values for this list will be set for each recipient.
// We don't validate that these have one of the MAPI defined values as we cannot tell
// if this call is being made to address a message. We don't care about this value if
// cDestFields is 0.
//
if (lpAdrParms->lpulDestComps
&& IsBadReadPtr(lpAdrParms->lpulDestComps, (UINT) lpAdrParms->cDestFields*sizeof(ULONG)))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->lpulDestComps"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
}
//
// lpContRestriction - This restriction, if there, gets applied to every contents table
// that is opened during the life of this dialog.
//
if (lpAdrParms->lpContRestriction && FBadRestriction(lpAdrParms->lpContRestriction))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->lpContRestriction"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
//
// lpHierRestriction - This restriction, if there, gets applied to the hierarchy table.
// It's very useful when done on PR_AB_PROVIDER_ID.
//
if (lpAdrParms->lpHierRestriction && FBadRestriction(lpAdrParms->lpHierRestriction))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->lpHierRestriction"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
//
// DIALOG_SDI
//
if (lpAdrParms->ulFlags & DIALOG_SDI)
{
//
// Only if we're SDI do we check these function pointers. They don't
// have to exist, although our current implementation of the dialogs will
// behave strangely without them.
//
if (lpAdrParms->lpfnDismiss && IsBadCodePtr((FARPROC)lpAdrParms->lpfnDismiss))
{
DebugTraceArg(IAB_Address, TEXT("Invalid lpAdrParm->lpfnDismiss"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
}
#endif // PARAMETER_VALIDATION
VerifyWABOpenExSession(lpIAB);
hr = HrShowAddressUI(
(LPADRBOOK)lpIAB,
lpIAB->lpPropertyStore->hPropertyStore,
lpulUIParam,
lpAdrParms,
lppAdrList);
#ifndef DONT_ADDREF_PROPSTORE
ReleasePropertyStore(lpIAB->lpPropertyStore);
exitNotAddRefed:
#endif
return hr;
}
//---------------------------------------------------------------------------
// Name: IAB_Details()
//
// Description:
// Parameters:
// Returns:
// Effects:
// Notes: ulFlags can be 0 or MAPI_UNICODE but the MAPI_UNICODE only affects
// the lpszButtonText which is not supported. Hence this function doesn't
// change any behaviour with MAPI_UNICODE flag.
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
IAB_Details(LPIAB lpIAB,
ULONG_PTR FAR * lpulUIParam,
LPFNDISMISS lpfnDismiss,
LPVOID lpvDismissContext,
ULONG cbEntryID,
LPENTRYID lpEntryID,
LPFNBUTTON lpfButtonCallback,
LPVOID lpvButtonContext,
LPTSTR lpszButtonText,
ULONG ulFlags)
{
SCODE sc;
HRESULT hr = hrSuccess;
BOOL bChangesMade = FALSE; //flags us if Details lead to any editing
BYTE bType;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
// LPMAPIERROR lpMAPIError = NULL;
// MAPIDLG_Details FAR *lpfnDetails;
// BOOL fInited;
#ifndef DONT_ADDREF_PROPSTORE
if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpIAB->lpPropertyStore)))) {
hr = ResultFromScode(sc);
goto exitNotAddRefed;
}
#endif
#ifdef PARAMETER_VALIDATION
// Make sure it's an IAB
//
if (BAD_STANDARD_OBJ(lpIAB, IAB_, Details, lpVtbl))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
/*
* Validate flags
*/
if (ulFlags & ~(MAPI_UNICODE | DIALOG_MODAL | DIALOG_SDI | WAB_ONEOFF_NOADDBUTTON))
{
/*
* Unknown flags
*/
DebugTraceArg(IAB_Details, TEXT("Unknown flags used"));
//return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
}
// Validate Parameters
if (!lpulUIParam
|| (lpulUIParam && IsBadWritePtr(lpulUIParam, sizeof(ULONG))))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (!cbEntryID
|| IsBadReadPtr(lpEntryID, (UINT) cbEntryID))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (lpfButtonCallback
&& (IsBadCodePtr((FARPROC) lpfButtonCallback)
|| (lpszButtonText
&& ((ulFlags & MAPI_UNICODE)
? IsBadStringPtrW((LPWSTR) lpszButtonText, (UINT) -1)
: IsBadStringPtrA((LPSTR)lpszButtonText, (UINT) -1)))))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if ((ulFlags & DIALOG_SDI) && IsBadCodePtr((FARPROC) lpfnDismiss))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
VerifyWABOpenExSession(lpIAB);
bType = IsWABEntryID(cbEntryID, lpEntryID, NULL, NULL, NULL, NULL, NULL);
if( (bType == 0) &&
cbEntryID && lpEntryID) // assume its valid ..
{
// its unlikely that anyone will ever give a template entry id to this
// function. Hence if we are here, we have some non-null cbEntryID
// and lpEntryid .. if we can open it, we can tell if its a mailuser
// or a distlist ...
// We'll have to open this entry and look at its ulObjectType
ULONG ulObjectType = 0;
LPMAPIPROP lpMailUser = NULL;
hr = lpIAB->lpVtbl->OpenEntry( lpIAB,
cbEntryID,
lpEntryID,
NULL,
0,
&ulObjectType,
(LPUNKNOWN * )&lpMailUser);
if(HR_FAILED(hr))
goto exit;
if (ulObjectType == MAPI_DISTLIST)
bType = WAB_DEF_DL;
else
bType = WAB_DEF_MAILUSER;
if(lpMailUser)
lpMailUser->lpVtbl->Release(lpMailUser);
}
if ((bType == WAB_DEF_MAILUSER) || (cbEntryID == 0))
{
hr = HrShowDetails((LPADRBOOK) lpIAB,
(HWND) *lpulUIParam,
lpIAB->lpPropertyStore->hPropertyStore,
0, NULL, //container EID
&cbEntryID,
&lpEntryID,
NULL,
SHOW_DETAILS,
MAPI_MAILUSER,
&bChangesMade);
}
else if (bType == WAB_DEF_DL)
{
hr = HrShowDetails((LPADRBOOK) lpIAB,
(HWND) *lpulUIParam,
lpIAB->lpPropertyStore->hPropertyStore,
0, NULL, //container EID
&cbEntryID,
&lpEntryID,
NULL,
SHOW_DETAILS,
MAPI_DISTLIST,
&bChangesMade);
}
else if ((bType == WAB_ONEOFF) || (bType == WAB_LDAP_MAILUSER))
{
//this may be a one-off entry
hr = HrShowOneOffDetails((LPADRBOOK) lpIAB,
(HWND) *lpulUIParam,
cbEntryID,
lpEntryID,
MAPI_MAILUSER,
NULL,
NULL,
(ulFlags & WAB_ONEOFF_NOADDBUTTON) ?
SHOW_ONE_OFF | WAB_ONEOFF_NOADDBUTTON : SHOW_ONE_OFF);
}
else
{
DebugTrace(TEXT("IAB_Details got unknown entryID type\n"));
hr = ResultFromScode(MAPI_E_INVALID_ENTRYID);
goto exit;
}
exit:
#ifndef DONT_ADDREF_PROPSTORE
ReleasePropertyStore(lpIAB->lpPropertyStore);
exitNotAddRefed:
#endif
// [PaulHi] 3/22/99 Raid 69651 If the DL property sheet changed, assume the title has
// changed as well and refresh the Tree View
if ( (bType == WAB_DEF_DL) && bChangesMade && lpIAB->hWndBrowse)
PostMessage(lpIAB->hWndBrowse, WM_COMMAND, (WPARAM) IDM_VIEW_REFRESH, 0);
return(hr);
}
//-----------------------------------------------------------------------------
// Synopsis: IAB_RecipOptions()
// Description:
// Resolve per Recipient Options.
//
// Parameters:
// [in] LPIAB lpIAB Pointer to AB object
// [in] ULONG ulUIParam Platform dependant UI parm
// [in] ULONG ulFlags Flags. UNICODE Flags
// [in/out] LPADRENTRY * lppRecip Recipient whose options are to be
// displayed
// Returns:
// HRESULT hr hrSuccess: if no problems. Also if no Recip
// Options found.
// Effects:
// Notes:
// - HrRecipOptions() will have to be modified to take a
// ulFlag parameter so that any string properties are returned
// Unicode if requested.
//
// - UNICODE currently not supported
//
// Revision:
//-----------------------------------------------------------------------------
STDMETHODIMP
IAB_RecipOptions(LPIAB lpIAB, ULONG_PTR ulUIParam, ULONG ulFlags,
LPADRENTRY lpRecip)
{
HRESULT hr;
#ifdef OLD_STUFF
SCODE sc = S_OK;
LPMALLOC lpMalloc = NULL;
LPXPLOGON lpXPLogon = NULL;
LPOPTIONDATA lpOptionData = NULL;
LPSTR lpszError = NULL;
LPSTR lpszAdrType = NULL;
LPPROPDATA lpPropData = NULL;
LPMAPIPROP lpIPropWrapped = NULL;
LPPROFSUP lpSup = NULL;
LPMAPITABLE lpDisplayTable = NULL;
OPTIONCALLBACK * pfnXPOptionCallback = NULL;
UINT idsError = 0;
HINSTANCE hinstXP = 0;
ULONG cProps;
LPSPropValue lpProp;
LPGUID lpRecipGuid;
LPSTR lpszTitle = NULL;
MAPIDLG_DoConfigPropsheet FAR *lpfnPropsheet;
LPMAPIERROR lpMapiError = NULL;
BOOL fInited;
#ifdef PARAMETER_VALIDATION
// Check to see if it has a jump table
if (IsBadReadPtr(lpIAB, sizeof(LPVOID)))
{
// No jump table found
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Check to see that it's IABs jump table
if (lpIAB->lpVtbl != &vtblIAB)
{
// Not my jump table
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Validate that the UI handle is good
if (ulUIParam && !IsWindow((HWND)ulUIParam))
{
DebugTraceArg(IAB_RecipOptions, TEXT("invalid window handle\n"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Validate flags
if (ulFlags & ~MAPI_UNICODE)
{
DebugTraceArg(IAB_RecipOptions, TEXT("reserved flags used\n"));
// return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
}
// Validate the ADRENTRY
if (IsBadWritePtr(lpRecip, sizeof(ADRENTRY))) // RAID 1967
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (!(lpRecip) || FBadRgPropVal((LPSPropValue)lpRecip->rgPropVals, (int)lpRecip->cValues))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpIAB->cs);
// We need the lpMalloc for the OptionCallback
lpMalloc = lpIAB->pSession->lpMalloc;
// Spin through the props and look for the PR_ENTRYID and PR_ADDRTYPE
lpProp = NULL;
cProps = lpRecip->cValues;
lpProp = LpValFindProp(PR_ENTRYID, cProps, lpRecip->rgPropVals);
if (!lpProp)
{
DebugTrace(TEXT("IAB_RecipOptions(): No EntryId found in AdrEntry prop array\n"));
hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
idsError = IDS_INVALID_PARAMETER;
goto exit;
}
// Get MAPI UID
lpRecipGuid = (LPGUID)((LPENTRYID)lpProp->Value.bin.lpb)->ab;
lpProp = NULL;
lpProp = LpValFindProp(PR_ADDRTYPE, cProps, lpRecip->rgPropVals);
if (lpProp)
{
if (PROP_TYPE(lpProp->ulPropTag) == PT_STRING8)
{
lpszAdrType = lpProp->Value.lpszA;
}
}
// Build the support object. Try using the Profile Support object.
if (HR_FAILED(hr = NewProfSup(lpIAB->pSession, &lpSup)))
{
idsError = IDS_NOT_ENOUGH_MEMORY;
DebugTrace(TEXT("IAB_RecipOptions(): error creating Support object\n"));
goto exit;
}
Assert(lpSup);
// Find out if there is any Option Data for us.
hr = HrGetRecipOptions(lpRecipGuid, lpszAdrType, &lpSup->muidSection,
&lpSup->muidService, &lpXPLogon, &lpOptionData);
if (GetScode(hr) == MAPI_E_NOT_FOUND)
{
// It's not really an error, just that no recip options exist for that
// recipient. Convert hr to a warning and exit.
hr = ResultFromScode(MAPI_W_ERRORS_RETURNED);
idsError = IDS_NO_RECIP_OPTIONS;
goto exit;
}
if (HR_FAILED(hr))
{
idsError = IDS_OPTIONS_DATA_ERROR;
DebugTrace(TEXT("IAB_RecipOptions(): Failure obtaining Option Data\n"));
goto exit;
}
Assert(lpXPLogon && lpOptionData);
// Get the XP callback function.
if (FAILED (ScMAPILoadProviderLibrary (lpOptionData->lpszDLLName, &hinstXP)))
{
SideAssert(sc = GetLastError());
idsError = IDS_CANT_INIT_PROVIDER;
DebugTrace(TEXT("IAB_RecipOptions(): error 0x%lx loading XP provider %s\n"),
sc, lpOptionData->lpszDLLName);
goto exit;
}
pfnXPOptionCallback = (OPTIONCALLBACK *)GetProcAddress(hinstXP,
(LPCSTR)lpOptionData->ulOrdinal);
if (!pfnXPOptionCallback)
{
DebugTrace(TEXT("IAB_RecipOptions(): error finding XPOptions callback\n"));
idsError = IDS_CANT_INIT_PROVIDER;
hr = ResultFromScode(MAPI_E_NOT_INITIALIZED);
goto exit;
}
// Create MAPIProp object
sc = CreateIProp((LPIID) &IID_IMAPIPropData,
MAPIAllocateBuffer,
MAPIAllocateMore,
MAPIFreeBuffer,
NULL,
&lpPropData);
if (FAILED(sc)) {
idsError = IDS_NOT_ENOUGH_MEMORY;
DebugTrace(TEXT("IAB_RecipOptions(): error creating IProp object\n"));
goto exit;
}
MAPISetBufferName(lpPropData, TEXT("lpPropData in IAB_RecipOptions"));
Assert(lpPropData);
// Copy over the Default props from the Options default props
if (lpOptionData->cOptionsProps && lpOptionData->lpOptionsProps)
{
cProps = lpOptionData->cOptionsProps;
lpProp = lpOptionData->lpOptionsProps;
if (HR_FAILED(hr = lpPropData->lpVtbl->SetProps(lpPropData, cProps, lpProp,
NULL)))
{
lpPropData->lpVtbl->GetLastError(lpPropData, hr, 0, &lpMapiError);
DebugTrace(TEXT("IAB_RecipOptions(): SetProps failed overall\n"));
goto exit;
}
}
// Copy over the props from the ADRENTRY to our IProp object
cProps = lpRecip->cValues;
lpProp = lpRecip->rgPropVals;
if (HR_FAILED(hr = lpPropData->lpVtbl->SetProps(lpPropData, cProps, lpProp,
NULL)))
{
lpPropData->lpVtbl->GetLastError(lpPropData, hr, 0, &lpMapiError);
DebugTrace(TEXT("IAB_RecipOptions(): SetProps failed overall\n"));
goto exit;
}
// Call the XP provider callback to get the wrapped IProp Interface
if (FAILED(sc = (*pfnXPOptionCallback)(hinstXP, lpMalloc,
OPTION_TYPE_RECIPIENT, lpOptionData->cbOptionsData,
lpOptionData->lpbOptionsData, (LPMAPISUP)lpSup,
(LPMAPIPROP)lpPropData, &lpIPropWrapped, &lpMapiError)))
{
DebugTrace(TEXT("IAB_RecipOptions(): failure calling XP Callback\n"));
goto exit;
}
Assert(lpIPropWrapped);
// Get PR_DISPLAY_DETAILS a MAPI Table object
if (HR_FAILED(hr = lpIPropWrapped->lpVtbl->OpenProperty(lpIPropWrapped,
PR_DETAILS_TABLE, (LPIID)&IID_IMAPITable, 0, MAPI_MODIFY,
(LPUNKNOWN *)&lpDisplayTable)))
{
lpIPropWrapped->lpVtbl->GetLastError(lpIPropWrapped, hr, 0, &lpMapiError);
DebugTrace(TEXT("IAB_RecipOptions(): failure opening PR_DISPLAY_DETAILS\n"));
goto exit;
}
Assert(lpDisplayTable);
// Initialize the common MAPI dialog DLL (MAPID??.DLL)
sc = ScGetDlgFunction(offsetof(JT_MAPIDLG, dlg_doconfigpropsheet),
(FARPROC FAR *)&lpfnPropsheet, &fInited);
if (FAILED(sc))
{
idsError = IDS_CANT_INIT_COMMON_DLG;
TraceSz("IAB_RecipOptions(): common dlg not init'd");
hr = ResultFromScode(sc);
goto exit;
}
// Loadstring the Subject Prefix text.
sc = ScStringFromIDS(MAPIAllocateBuffer, 0, IDS_RECIPIENT_OPTIONS,
&lpszTitle);
if (FAILED(sc))
{
hr = ResultFromScode(sc);
DebugTrace(TEXT("IAB_RecipOptions(): OOM for prop sheet title string\n"));
goto exit;
}
LeaveCriticalSection(&lpIAB->cs);
// Call into MAPIDLG_DoConfigPropSheet...
hr = (*lpfnPropsheet)(ulUIParam,
ulFlags,
lpszTitle,
0,
1,
&lpDisplayTable,
&lpIPropWrapped,
&lpMapiError);
if (fInited)
CloseMapidlg();
EnterCriticalSection(&lpIAB->cs);
if (HR_FAILED(hr))
{
// $ Internal fixup to return error info in this API call so it matches the
// the other methods.
DebugTrace(TEXT("IAB_RecipOptions(): DoConfigPropSheet error\n"));
goto exit;
}
// From the Wrapped Props we'll rebuild a new ADRENTRY prop array
// and pass it pack to the Client.
lpProp = NULL;
if (HR_FAILED(hr = lpIPropWrapped->lpVtbl->GetProps(lpIPropWrapped, NULL,
MAPI_UNICODE, // ansi
&cProps, &lpProp)))
{
lpIPropWrapped->lpVtbl->GetLastError(lpIPropWrapped, hr, 0, &lpMapiError);
DebugTrace(TEXT("IAB_RecipOptions(): GetProps on new wrapped IProps failed.\n"));
goto exit;
}
Assert(cProps && lpProp);
// Free up the old ADRENTRY prop array and hook up the new one
FreeBufferAndNull(&(lpRecip->rgPropVals));
lpRecip->rgPropVals = lpProp;
lpRecip->cValues = cProps;
exit: // and clean up
UlRelease(lpSup);
UlRelease(lpDisplayTable);
UlRelease(lpIPropWrapped);
// Free the XP Provider lib
#ifdef WIN32
if (hinstXP)
#else
if (hinstXP >= HINSTANCE_ERROR)
#endif
{
FreeLibrary(hinstXP);
}
UlRelease(lpPropData);
FreeBufferAndNull(&lpOptionData);
FreeBufferAndNull(&lpszTitle);
if (sc && !(hr))
hr = ResultFromScode(sc);
if (hr)
SetMAPIError(lpIAB, hr, idsError, NULL, 0, 0,
ulFlags & MAPI_UNICODE, lpMapiError);
FreeBufferAndNull(&lpMapiError);
LeaveCriticalSection(&lpIAB->cs);
#endif
hr = ResultFromScode(MAPI_E_NO_SUPPORT);
DebugTraceResult(SESSOBJ_MessageOptions, hr);
return hr;
}
//-----------------------------------------------------------------------------
// Synopsis: IAB_QueryDefaultRecipOpt()
//
// Description: Returns the XP provider registered default options property
// list.
//
// Parameters:
// [in] LPIAB lpIAB Pointer to AB object
// [in] LPTSTR lpszAdrType
// [in] ULONG ulFlags Flags. UNICODE Flags
// [out] ULONG FAR * lpcValues
// [out] LPSPropValue FAR * lppOptions
//
// Returns:
// HRESULT hr hrSuccess: if no problems. Also if no Recip
// Options found.
// Effects:
// Notes:
// - Unicode not implemented.
//
// Revision:
//-----------------------------------------------------------------------------
STDMETHODIMP
IAB_QueryDefaultRecipOpt(LPIAB lpIAB, LPTSTR lpszAdrType, ULONG ulFlags,
ULONG FAR * lpcValues, LPSPropValue FAR * lppOptions)
{
HRESULT hr = hrSuccess;
#ifdef OLD_STUFF
SCODE sc = S_OK;
LPXPLOGON lpXPLogon = NULL;
LPOPTIONDATA lpOptionData = NULL;
LPSPropValue lpPropCopy = NULL;
UINT idsError = 0;
LPSTR lpszAdrTypeA = NULL;
MAPIUID muidSection;
MAPIUID muidService;
#ifdef PARAMETER_VALIDATION
/*
* Check to see if it has a jump table
*/
if (IsBadReadPtr(lpIAB, sizeof(LPVOID)))
{
/*
* No jump table found
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
/*
* Check to see that it's IABs jump table
*/
if (lpIAB->lpVtbl != &vtblIAB)
{
/*
* Not my jump table
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
/*
* Check that return params can be written
*/
if (IsBadWritePtr(lpcValues, sizeof(ULONG))
|| IsBadWritePtr(lppOptions, sizeof(LPSPropValue)))
{
/*
* Bad output parameters
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
/*
* Validate flags
*/
if (ulFlags & ~MAPI_UNICODE)
{
/*
* Unknown flags
*/
return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
}
if (IsBadStringPtrA((LPCSTR)lpszAdrType, (UINT)-1))
{
/*
* Bad input string
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpIAB->cs);
hr = HrGetRecipOptions(NULL, (LPSTR)lpszAdrType, &muidSection, &muidService,
&lpXPLogon, &lpOptionData);
if (GetScode(hr) == MAPI_E_NOT_FOUND)
{
// It's not an error, just that no recip options exist for that
// adrtype. Convert hr to hrSucces and exit.
hr = hrSuccess;
goto exit;
}
Assert(lpXPLogon && lpOptionData);
if (HR_FAILED(hr))
{
idsError = IDS_OPTIONS_DATA_ERROR;
DebugTrace(TEXT("IAB_QueryDefaultRecipOpt(): Failure obtaining Option Data\n"));
goto exit;
}
// Find out if we have any default options to return.
if (lpOptionData->cOptionsProps && lpOptionData->lpOptionsProps)
{
// Copy out the props from OptionData struct into new memory
if (FAILED(sc = ScDupPropset((int)lpOptionData->cOptionsProps,
lpOptionData->lpOptionsProps, MAPIAllocateBuffer,
&lpPropCopy)))
{
idsError = IDS_NOT_ENOUGH_MEMORY,
DebugTrace(TEXT("IAB_QueryDefaultRecipOpt(): Failure to copy prop set\n"));
goto exit;
}
}
*lpcValues = lpOptionData->cOptionsProps;
*lppOptions = lpPropCopy;
exit:
FreeBufferAndNull(&lpOptionData);
if (sc && !hr)
hr = ResultFromScode(sc);
if (hr)
SetMAPIError(lpIAB, hr, idsError, NULL, 0, 0, 0, NULL);
LeaveCriticalSection(&lpIAB->cs);
#endif
hr = ResultFromScode(MAPI_E_NO_SUPPORT);
DebugTraceResult(IAB_QueryDefaultRecipOpt, hr);
return hr;
}
//---------------------------------------------------------------------------
// Name: IAB_GetPAB()
// Description:
// This API normally returns what would be the default WAB Container
// In pre-IE5 implementations of WAB, there is only 1 container which is
// returned by this statement ..
// In IE5 WAB, the WAB can be running in profile mode or not in profile mode
// If the WAB is not in profile mode it runs same as before (GetPAB returns a
// single container that has all the WAB contents in it)
// If the WAB is in profile mode and has no current user, it runs same as before (GetPAB
// returns a single container that has all the WAB contents in it)
// If the WAB is in profile mode and has a user, the container returned here
// corresponds to the user's contact folder - thus external apps would manipulate
// directly into the users contact folder and not into other folders
// Internally, however, the WAB may want to have a "Shared Contacts" container which has
// stuff not in other folders. This shared contacts is needed for the WAB UI in both
// with-user and without-user modes .. to distinguish between when we want the
// shared contacts folder vs. when we want the all-contacts PAB or user's folder PAB,
// we define 2 internal functions that set the PAB EID to a special setting..
// The assumption is that GetPAB is always followed by OpenEntry to get the container ..
// .. if it is, then in OpenEntry we can check the PAB EID and determine what kind of
// container to create ..
// If lpContainer->pmbinOlk = NULL, this container contains all WAB contents
// If lpContainer->pmbinOlk != NULL but lpContainer->pmbinOlk.cb = 0 and
// lpContainer->pmbinOlk->lpb = NULL, this is the "Shared Contacts" folder
// If nothing is NULL, then this is the user's folder ..
//
// For the special EID, we set *lpcbEntryID == SIZEOF_WAB_ENTRYID and
// *lppEntryID to szEmpty (This is a hack but there is no flag param here and it
// should be safe for internal use only)
//
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
/*
- SetVirtualPABEID
- When calling GetPAB, we want to sometimes specify getting the virtual PAB
* Folder instead of getting the Current User's Folder which is what
* would be returned if this was a profile session. to somehow indicate to
* GetPAB what folder we want, we have a very special EID combination that
* needs to be hndled very carefully .. for this the cbSize if 4 and
* the lpEntryID is the static const string szEmpty
*/
// This function is added here so we can keep it linked to how GetPAB works
void SetVirtualPABEID(LPIAB lpIAB, ULONG * lpcb, LPENTRYID * lppb)
{
//if(bAreWABAPIProfileAware(lpIAB))// && bIsThereACurrentUser(lpIAB))
{
*lpcb = SIZEOF_WAB_ENTRYID;
*lppb = (LPENTRYID) szEmpty;
}
}
// This function determines if the EID denotes a special virtual root PAB
BOOL bIsVirtualPABEID(ULONG cbEID, LPENTRYID lpEID)
{
return (cbEID == SIZEOF_WAB_ENTRYID && szEmpty == (LPTSTR) lpEID);
}
STDMETHODIMP
IAB_GetPAB (LPIAB lpIAB,
ULONG * lpcbEntryID,
LPENTRYID * lppEntryID)
{
HRESULT hr;
ULONG cbEID = 0;
LPENTRYID lpEID = NULL;
BOOL bSharedPAB = FALSE;
#ifdef PARAMETER_VALIDATION
// Check to see if it has a jump table
if (IsBadReadPtr(lpIAB, sizeof(LPVOID))) {
// No jump table found
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
// Check to see that it's IABs jump table
if (lpIAB->lpVtbl != &vtblIAB) {
// Not my jump table
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
if (IsBadWritePtr(lpcbEntryID, sizeof(ULONG)) ||
IsBadWritePtr(lppEntryID, sizeof(LPENTRYID)))
{
// Bad parameters.
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
}
#endif // PARAMETER_VALIDATION
VerifyWABOpenExSession(lpIAB);
cbEID = *lpcbEntryID;
lpEID = *lppEntryID;
if(bIsVirtualPABEID(cbEID, lpEID))
{
// this is a special case where we asked for an over-ride of the GetPAB behaviour ..
// in this case, we don't do anything
bSharedPAB =TRUE;
cbEID = 0; lpEID = NULL;
}
else
if(bAreWABAPIProfileAware(lpIAB) && bIsThereACurrentUser(lpIAB))
{
// if this is a user-session then
cbEID = lpIAB->lpWABCurrentUserFolder->sbEID.cb;
lpEID = (LPENTRYID)lpIAB->lpWABCurrentUserFolder->sbEID.lpb;
}
else
{
cbEID = 0;
lpEID = NULL;
}
*lppEntryID = NULL;
*lpcbEntryID = 0;
if(!cbEID && !lpEID)
{
BYTE bPABType = bSharedPAB ? WAB_PABSHARED : WAB_PAB;
if (HR_FAILED(hr = CreateWABEntryID( bPABType, // Create WAB's PAB entryid
lpEID, NULL, NULL,
cbEID, 0,
NULL, // lpRoot (allocmore here)
lpcbEntryID, // returned cbEntryID
lppEntryID)))
{
goto out;
}
}
else
{
if(!MAPIAllocateBuffer(cbEID, (LPVOID *)lppEntryID))
{
*lpcbEntryID = cbEID;
CopyMemory(*lppEntryID, lpEID, cbEID);
}
}
MAPISetBufferName(*lppEntryID, TEXT("WAB PAB Entry ID"));
hr = hrSuccess;
out:
return(hr);
}
//---------------------------------------------------------------------------
// Name: IAB_SetPAB()
// Description:
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
IAB_SetPAB (LPIAB lpIAB,
ULONG cbEntryID,
LPENTRYID lpEntryID)
{
return(ResultFromScode(MAPI_E_NO_SUPPORT));
}
//---------------------------------------------------------------------------
// Name: IAB_GetDefaultDir()
//
// Description:
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
IAB_GetDefaultDir (LPIAB lpIAB,
ULONG * lpcbEntryID,
LPENTRYID * lppEntryID)
{
#ifdef OLD_STUFF
HRESULT hr = hrSuccess;
SCODE sc;
ULONG cValues;
LPSPropValue lpPropVal = NULL;
LPSBinary lpbinEntryID = NULL;
#ifdef PARAMETER_VALIDATION
/*
* Check to see if it has a jump table
*/
if (IsBadReadPtr(lpIAB, sizeof(LPVOID)))
{
/*
* No jump table found
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
/*
* Check to see that it's IABs jump table
*/
if (lpIAB->lpVtbl != &vtblIAB)
{
/*
* Not my jump table
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (IsBadWritePtr(lpcbEntryID, sizeof(ULONG)) ||
IsBadWritePtr(lppEntryID, sizeof(LPENTRYID)))
{
/*
* Bad parameters.
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpIAB->cs);
*lppEntryID = NULL;
*lpcbEntryID = 0;
//
// Check to see if IAdrBook already has this info...
//
if (lpIAB->lpEntryIDDD)
{
// If so, copy it and we're done.
if ((sc = MAPIAllocateBuffer(lpIAB->cbEntryIDDD,
(LPVOID *) lppEntryID))
!= S_OK)
{
hr = ResultFromScode(sc);
goto out;
}
MAPISetBufferName(*lppEntryID, TEXT("Entry ID"));
MemCopy(*lppEntryID, lpIAB->lpEntryIDDD, (UINT)lpIAB->cbEntryIDDD);
*lpcbEntryID = lpIAB->cbEntryIDDD;
hr = hrSuccess;
goto out;
}
// If not...
//
// Retrieve PR_AB_DEFAULT_DIR from MAPIs default profile section
//
if (HR_FAILED(hr = ResultFromScode(IAB_ScGetABProfSectProps(
lpIAB,
&SPT_DD,
&cValues,
&lpPropVal))))
{
SetMAPIError(lpIAB, hr, IDS_NO_DEFAULT_DIRECTORY, NULL, 0,
0, 0, NULL);
goto out;
}
//
// Did I get it?? Is it in the hierarchy??
//
if (PROP_TYPE(lpPropVal->ulPropTag) == PT_ERROR ||
!FContainerInHierarchy(lpIAB,
lpPropVal->Value.bin.cb,
(LPENTRYID) lpPropVal->Value.bin.lpb))
{
// No, look for the first global read-only container with Recipients.
hr = HrFindDirectory(lpIAB, 0, AB_RECIPIENTS | AB_UNMODIFIABLE,
&lpbinEntryID, NULL, NULL);
if (HR_FAILED(hr))
{
if (GetScode(hr) != MAPI_E_NOT_FOUND)
{
// Assume HrFindDirectory set the last error sz
goto out;
}
// Didn't find any read-only containers, how about read write?
hr = HrFindDirectory(lpIAB, 0, AB_RECIPIENTS | AB_MODIFIABLE,
&lpbinEntryID, NULL, NULL);
if (HR_FAILED(hr))
{
// Assume HrFindDirectory set the last error sz
goto out;
}
}
sc = MAPIAllocateBuffer(lpbinEntryID->cb, lppEntryID);
if (FAILED(sc))
goto out;
MemCopy(*lppEntryID, lpbinEntryID->lpb, lpbinEntryID->cb);
*lpcbEntryID = lpbinEntryID->cb;
MAPISetBufferName(*lppEntryID, TEXT("Default Dir EntryID"));
}
else
{
// Yes? Copy it and return it to the caller
hr = hrSuccess; // Don't return warnings.
if ((sc = MAPIAllocateBuffer(lpPropVal->Value.bin.cb,
(LPVOID *) lppEntryID)) != S_OK)
{
hr = ResultFromScode(sc);
goto out;
}
MAPISetBufferName(*lppEntryID, TEXT("Entry ID"));
MemCopy(*lppEntryID, lpPropVal->Value.bin.lpb,
(UINT)lpPropVal->Value.bin.cb);
*lpcbEntryID = lpPropVal->Value.bin.cb;
}
// Cache default directory in Iadrbook.
sc = MAPIAllocateBuffer(*lpcbEntryID, (LPVOID *) &(lpIAB->lpEntryIDDD));
if (FAILED(sc))
{
hr = ResultFromScode(sc);
goto out;
}
MAPISetBufferName(lpIAB->lpEntryIDDD, TEXT("cached IAB Entry ID"));
// Set IAdrBooks Default directory
MemCopy(lpIAB->lpEntryIDDD, *lppEntryID,(UINT)*lpcbEntryID);
lpIAB->cbEntryIDDD = *lpcbEntryID;
out:
FreeBufferAndNull(&lpPropVal);
FreeBufferAndNull(&lpbinEntryID);
LeaveCriticalSection (&lpIAB->cs);
// MAPI_E_NOT_FOUND is not an error
if (MAPI_E_NOT_FOUND == GetScode(hr))
hr = hrSuccess;
DebugTraceResult(IAB_GetDefaultDir, hr);
return hr;
#endif
return(IAB_GetPAB(lpIAB, lpcbEntryID, lppEntryID));
}
//---------------------------------------------------------------------------
// Name: IAB_SetDefaultDir()
//
// Description:
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
IAB_SetDefaultDir (LPIAB lpIAB,
ULONG cbEntryID,
LPENTRYID lpEntryID)
{
#ifdef OLD_STUFF
HRESULT hr = hrSuccess;
SPropValue spvDD;
SCODE sc;
#ifdef PARAMETER_VALIDATION
/*
* Check to see if it has a jump table
*/
if (IsBadReadPtr(lpIAB, sizeof(LPVOID)))
{
/*
* No jump table found
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
/*
* Check to see that it's IABs jump table
*/
if (lpIAB->lpVtbl != &vtblIAB)
{
/*
* Not my jump table
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (IsBadReadPtr(lpEntryID, (UINT)cbEntryID)
|| (cbEntryID < sizeof (LPENTRYID)))
{
/*
* Not my jump table
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
EnterCriticalSection(&lpIAB->cs);
//
// Check to see if IAdrBook already has the Default Dir
//
if ((lpEntryID == lpIAB->lpEntryIDDD) ||
((cbEntryID == lpIAB->cbEntryIDDD) &&
(!memcmp(lpEntryID, lpIAB->lpEntryIDDD, (UINT)cbEntryID))))
{
// If so, all done.
goto out;
}
//
// Free the old entryid
//
if (lpIAB->lpEntryIDDD)
{
FreeBufferAndNull(&(lpIAB->lpEntryIDDD));
lpIAB->lpEntryIDDD = NULL;
lpIAB->cbEntryIDDD = 0;
}
//
// Allocate space for a new entry id
//
if ((sc = MAPIAllocateBuffer(cbEntryID, (LPVOID *) &(lpIAB->lpEntryIDDD)))
!= S_OK)
{
hr = ResultFromScode(sc);
goto out;
}
MAPISetBufferName(lpIAB->lpEntryIDDD, TEXT("cached IAB Entry ID"));
//
// Set IAdrBooks Default directory
//
MemCopy(lpIAB->lpEntryIDDD, lpEntryID, (UINT)cbEntryID);
lpIAB->cbEntryIDDD = cbEntryID;
//
// Set the PR_AB_DEFAULT_DIR
// If it fails, continue anyway.
//
spvDD.ulPropTag = PR_AB_DEFAULT_DIR;
spvDD.Value.bin.cb = cbEntryID;
spvDD.Value.bin.lpb = (LPBYTE) lpEntryID;
(void) IAB_ScSetABProfSectProps(lpIAB, 1, &spvDD);
out:
LeaveCriticalSection(&lpIAB->cs);
return hr;
#endif
// BUGBUG: We need to fool Word into thinking this call succeeded.
return(SUCCESS_SUCCESS);
}
// #pragma SEGMENT(IAdrBook2)
//---------------------------------------------------------------------------
// Name: IAB_GetSearchPath()
//
// Description:
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
IAB_GetSearchPath(LPIAB lpIAB,
ULONG ulFlags,
LPSRowSet FAR * lppSearchPath)
{
HRESULT hr = E_FAIL;
ULONG ulObjectType = 0;
LPROOT lpRoot = NULL;
LPMAPITABLE lpContentsTable = NULL;
LPSRowSet lpSRowSet = NULL;
ULONG i=0,j=0;
ULONG ulContainerCount = 0;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
#ifdef PARAMETER_VALIDATION
// Make sure it's an IAB
//
if (BAD_STANDARD_OBJ(lpIAB, IAB_, Address, lpVtbl))
{
DebugTraceArg(IAB_Address, TEXT("Bad vtable"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// [PaulHi] 1/28/99 Raid 58495
if (IsBadWritePtr(lppSearchPath, sizeof(LPSRowSet)))
{
DebugTrace(TEXT("ERROR: IAB_GetSearchPath - invalid out pointer"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif
VerifyWABOpenExSession(lpIAB);
hr = lpIAB->lpVtbl->OpenEntry( lpIAB,
0,
NULL,
NULL,
0,
&ulObjectType,
(LPUNKNOWN *) &lpRoot );
if (HR_FAILED(hr))
{
DebugPrintError(( TEXT("OpenEntry Failed: %x\n"),hr));
goto out;
}
hr = lpRoot->lpVtbl->GetContentsTable( lpRoot,
ulFlags,
&lpContentsTable);
if (HR_FAILED(hr))
{
DebugPrintError(( TEXT("GetContentsTable Failed: %x\n"),hr));
goto out;
}
// Set the columns to the bare minimum
hr = lpContentsTable->lpVtbl->SetColumns(lpContentsTable,
(LPSPropTagArray)&irnColumns,
0);
// This contentstable contains a list of all the containers,
// which is basically the local container(s) followed by
// all the LDAP containers ...
//
// By doing a QueryAllRows we will get an allocated SRowSet
// which we will reuse and free the remaining elements of it
//
hr = HrQueryAllRows(lpContentsTable,
NULL,
NULL,
NULL,
0,
&lpSRowSet);
if (HR_FAILED(hr))
{
DebugPrintError(( TEXT("HrQueryAllRows Failed: %x\n"),hr));
goto out;
}
// Now we want to return only the WAB container(s) and the
// only those LDAP containers that have been chosen for
// doing a ResolveNames operation ..
if (pt_bIsWABOpenExSession) {
ulContainerCount = lpIAB->lpPropertyStore->colkci;
Assert(ulContainerCount);
} else
ulContainerCount = 1; // always return WAB_PAB so minimum is one
// Do a restriction on the contentstable to get resolvename
// LDAP containers ..
{
SRestriction resAnd[2]; // 0 = LDAP, 1 = ResolveFlag
SRestriction resLDAPResolve;
SPropValue ResolveFlag;
ULONG cRows;
// Restrict: Only show LDAP containers with Resolve TRUE
resAnd[0].rt = RES_EXIST;
resAnd[0].res.resExist.ulReserved1 = 0;
resAnd[0].res.resExist.ulReserved2 = 0;
resAnd[0].res.resExist.ulPropTag = (ulFlags & MAPI_UNICODE) ? // <note> assumes UNICODE defined
PR_WAB_LDAP_SERVER :
CHANGE_PROP_TYPE( PR_WAB_LDAP_SERVER, PT_STRING8);
ResolveFlag.ulPropTag = PR_WAB_RESOLVE_FLAG;
ResolveFlag.Value.b = TRUE;
resAnd[1].rt = RES_PROPERTY;
resAnd[1].res.resProperty.relop = RELOP_EQ;
resAnd[1].res.resProperty.ulPropTag = PR_WAB_RESOLVE_FLAG;
resAnd[1].res.resProperty.lpProp = &ResolveFlag;
resLDAPResolve.rt = RES_AND;
resLDAPResolve.res.resAnd.cRes = 2;
resLDAPResolve.res.resAnd.lpRes = resAnd;
hr = lpContentsTable->lpVtbl->Restrict(lpContentsTable,
&resLDAPResolve,
0);
if (HR_FAILED(hr))
{
DebugTraceResult( TEXT("RootTable: Restrict"), hr);
goto out;
}
// Since the number of resolve-LDAP-Containers is less than the
// set of all the containers ... we can safely use our LPSRowset
// allocated structure to get the items we want without worrying
// about overruns ..
{
ULONG cRows = 1;
while(cRows)
{
LPSRowSet lpRow = NULL;
hr = lpContentsTable->lpVtbl->QueryRows(lpContentsTable,
1, //one row at a time
0,
&lpRow);
if(HR_FAILED(hr))
{
DebugTraceResult( TEXT("ResolveName:QueryRows"), hr);
cRows = 0;
}
else if (lpRow)
{
cRows = lpRow->cRows;
if (cRows)
{
// replace a container in the lpSRowSet list with
// this one ...
FreeBufferAndNull((LPVOID *) (&lpSRowSet->aRow[ulContainerCount].lpProps));
lpSRowSet->aRow[ulContainerCount].cValues = lpRow->aRow[0].cValues;
lpSRowSet->aRow[ulContainerCount].lpProps = lpRow->aRow[0].lpProps;
lpRow->aRow[0].cValues = 0;
lpRow->aRow[0].lpProps = NULL;
ulContainerCount++;
}
FreeProws(lpRow);
}
else
{
cRows = 0;
}
} // while cRows
//Free any extra memory we might have got ...
for (i=ulContainerCount;i<lpSRowSet->cRows;i++)
{
FreeBufferAndNull((LPVOID *) (&lpSRowSet->aRow[i].lpProps));
}
lpSRowSet->cRows = ulContainerCount;
}
}
hr = S_OK;
*lppSearchPath = lpSRowSet;
out:
if(lpContentsTable)
lpContentsTable->lpVtbl->Release(lpContentsTable);
if(lpRoot)
lpRoot->lpVtbl->Release(lpRoot);
return(hr);
}
//---------------------------------------------------------------------------
// Name: IAB_SetSearchPath()
// Description:
// Sets new searchpath in the user's profile.
// Special case empty or NULL rowset by deleting search path
// property from the profile.
//
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
IAB_SetSearchPath(LPIAB lpIAB,
ULONG ulFlags,
LPSRowSet lpSearchPath)
{
#ifdef OLD_STUFF
SCODE sc = SUCCESS_SUCCESS;
LPSBinary lpargbinDirEntryIDs = NULL;
LPSRow lprow;
LPSBinary lpbin;
UINT idsErr = 0;
#ifdef PARAMETER_VALIDATION
/*
* Check to see if it has a jump table
*/
if (IsBadReadPtr(lpIAB, sizeof(LPVOID)))
{
/*
* No jump table found
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
/*
* Check to see that it's IABs jump table
*/
if (lpIAB->lpVtbl != &vtblIAB)
{
/*
* Not my jump table
*/
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (FBadRowSet(lpSearchPath))
{
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
if (ulFlags) {
//
// No flags are defined for this call
//
return(ResultFromScode(MAPI_E_UNKNOWN_FLAGS));
}
EnterCriticalSection(&lpIAB->cs);
if (! lpSearchPath || ! lpSearchPath->cRows) {
sc = IAB_ScDeleteABProfSectProps(lpIAB,
(LPSPropTagArray)&ptagaABSearchPath);
// Clear the searchpath cache
#if defined (WIN32) && !defined (MAC)
if (fGlobalCSValid) {
EnterCriticalSection(&csMapiSearchPath);
} else {
DebugTrace(TEXT("IAB_SetSearchPath: MAPI32.DLL already detached.\n"));
}
#endif
FreeBufferAndNull(&(lpIAB->lpspvSearchPathCache));
#if defined (WIN32) && !defined (MAC)
if (fGlobalCSValid) {
LeaveCriticalSection(&csMapiSearchPath);
} else {
DebugTrace(TEXT("IAB_SetSearchPath: MAPI32.DLL got detached.\n"));
}
#endif
goto ret;
}
if (FAILED(sc = MAPIAllocateBuffer(lpSearchPath->cRows * sizeof(SBinary),
(LPVOID FAR *) &lpargbinDirEntryIDs))) {
DebugTrace(TEXT("IAB::SetSearchPath() - Error allocating space for search path array (SCODE = 0x%08lX)\n"), sc);
idsErr = IDS_NOT_ENOUGH_MEMORY;
goto err;
}
MAPISetBufferName(lpargbinDirEntryIDs, TEXT("IAB Search Path Array"));
// Convert the row set into an array of SBinarys
lprow = lpSearchPath->aRow + lpSearchPath->cRows;
lpbin = lpargbinDirEntryIDs + lpSearchPath->cRows;
while (lprow--, lpbin-- > lpargbinDirEntryIDs) {
//$??? Can I rely on the first column being the EntryID?
//$ No. - BJD
SPropValue *lpProp = PpropFindProp(lprow->lpProps, lprow->cValues, PR_ENTRYID);
if (!lpProp) {
DebugTrace(TEXT("IAB::SetSearchPath() - Row passed without PR_ENTRYID.\n"));
sc = MAPI_E_MISSING_REQUIRED_COLUMN;
goto err;
}
*lpbin = lpProp->Value.bin;
}
// Set the search path
sc = IAB_ScSetSearchPathI(lpIAB, lpSearchPath->cRows, lpargbinDirEntryIDs);
if (FAILED(sc)) {
DebugTrace(TEXT("IAB::SetSearchPath() - Error setting search path (SCODE = 0x%08lX)\n"), sc);
idsErr = IDS_SET_SEARCH_PATH;
goto err;
}
ret:
LeaveCriticalSection(&lpIAB->cs);
FreeBufferAndNull(&lpargbinDirEntryIDs);
DebugTraceSc(IAB_SetSearchPath, sc);
return(ResultFromScode(sc));
err:
SetMAPIError(lpIAB, ResultFromScode(sc), idsErr, NULL, 0, 0, 0, NULL);
goto ret;
return(ResultFromScode(sc));
#endif
return(ResultFromScode(MAPI_E_NO_SUPPORT));
}
//----------------------------------------------------------------------------
// Synopsis: IAB_PrepareRecips()
//
// Description:
// Calls each registered AB Provider with PrepareRecips.
// The providers convert short entryids to longterm entryids
// and ensures that the columnset contains the property tags
// identified in lpPropTagArray.
//
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
// Now check to see if enough info has been provided to avoid
// calling each registerd provider. RAID 5291
//
//----------------------------------------------------------------------------
STDMETHODIMP
IAB_PrepareRecips( LPIAB lpIAB,
ULONG ulFlags,
LPSPropTagArray pPropTagArray,
LPADRLIST pRecipList)
{
#ifdef OLD_STUFF
#ifdef PARAMETER_VALIDATION
// Check to see if it has a jump table
if (IsBadReadPtr(lpIAB, sizeof(LPVOID)))
{
// No jump table found
DebugTraceArg(IAB_PrepareRecips, TEXT("Bad vtable"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Check to see that it's IABs jump table
if (lpIAB->lpVtbl != &vtblIAB)
{
// Not my jump table
DebugTraceArg(IAB_PrepareRecips, TEXT("Bad vtable"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// validate the prop tag array
if (lpPropTagArray && FBadColumnSet(lpPropTagArray))
{
DebugTraceArg(IAB_PrepareRecips, TEXT("Bad PropTag Array"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
if (!lpRecipList || FBadAdrList(lpRecipList))
{
DebugTraceArg(IAB_PrepareRecips, TEXT("Bad ADRLIST"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
// Make sure we've got a valid lpSession
hr = HrCheckSession(0, lpIAB->pSession);
if (HR_FAILED(hr))
{
DebugTraceArg(IAB_PrepareRecips, TEXT("Bad Session Object"));
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}
#endif // PARAMETER_VALIDATION
#endif // oldstuff
HRESULT hr = hrSuccess;
ULONG ulRecip;
ULONG ulProp;
ULONG ulObjType;
ULONG cValues;
ULONG cTotal;
LPSPropValue pspv = NULL, pspvEID = NULL;
LPADRENTRY pRecipEntry;
LPMAILUSER pMailUser = NULL;
ULONG i = 0;
LPSPropValue lpPropArrayNew = NULL;
ULONG ulcPropsNew = 0;
SCODE sc;
Assert(pRecipList);
if (!pRecipList)
return(MAPI_E_INVALID_PARAMETER);
// Since our entry id's are always long-term, we are done if
// no additional properties are specified.
if (!pRecipList->cEntries || !pPropTagArray)
return S_OK;
EnterCriticalSection(&lpIAB->cs);
VerifyWABOpenExSession(lpIAB);
for (ulRecip = 0; ulRecip < pRecipList->cEntries; ulRecip++)
{
pspvEID = NULL;
pRecipEntry = &(pRecipList->aEntries[ulRecip]);
for(i=0;i<pRecipEntry->cValues;i++)
{
if(pRecipEntry->rgPropVals[i].ulPropTag == PR_ENTRYID)
{
pspvEID = &(pRecipEntry->rgPropVals[i]);
break;
}
}
// Ignore unresolved entries
if (!pspvEID)
continue;
// Open the entry
if (FAILED(lpIAB->lpVtbl->OpenEntry(lpIAB,
pspvEID->Value.bin.cb,
(LPENTRYID)pspvEID->Value.bin.lpb,
&IID_IMailUser, 0,
&ulObjType, (LPUNKNOWN *)&pMailUser)))
continue;
Assert((ulObjType == MAPI_MAILUSER) || (ulObjType == MAPI_DISTLIST));
// Get the requested props
hr = pMailUser->lpVtbl->GetProps(pMailUser, pPropTagArray, MAPI_UNICODE, &cValues, &pspv);
pMailUser->lpVtbl->Release(pMailUser);
if (FAILED(hr))
continue;
if(cValues && pspv)
{
sc = ScMergePropValues( cValues,
pspv,
pRecipEntry->cValues,
pRecipEntry->rgPropVals,
&ulcPropsNew,
&lpPropArrayNew);
if (sc != S_OK)
{
hr = ResultFromScode(sc);
goto out;
}
}
// We're done with this now
FreeBufferAndNull(&pspv);
pspv = NULL;
// Replace the props in the address list
FreeBufferAndNull((LPVOID *) (&pRecipEntry->rgPropVals));
pRecipEntry->rgPropVals = lpPropArrayNew;
pRecipEntry->cValues = ulcPropsNew;
lpPropArrayNew = NULL;
} // for
hr = hrSuccess;
out:
if (lpPropArrayNew)
FreeBufferAndNull(&lpPropArrayNew);
if (pspv)
FreeBufferAndNull(&pspv);
LeaveCriticalSection(&lpIAB->cs);
return hr;
}
#define MAX_DIGITS_ULONG_10 10 // 10 digits max in a ULONG base 10
/***************************************************************************
Name : GetNewPropTag
Purpose : Gets the next valid named PropTag for this property store.
Parameters: lpgnp -> GUID_NAMED_PROPS containing all named props for
this store.
ulEntryCount = number of GUIDs in lpgnp
Returns : returns the next valid PropTag value. If 0, there are no
more named properties. (This would be bad.)
Comment :
***************************************************************************/
ULONG GetNewPropTag(LPGUID_NAMED_PROPS lpgnp, ULONG ulEntryCount) {
static WORD wPropIDNext = 0;
ULONG j, k;
if (wPropIDNext == 0) {
// look through the current named props
// Since we don't allow removing named prop ids
// we always increment past the largest ID in use.
for (j = 0; j < ulEntryCount; j++) {
for (k = 0; k < lpgnp[j].cValues; k++) {
wPropIDNext = max(wPropIDNext, (WORD)PROP_ID(lpgnp[j].lpnm[k].ulPropTag));
}
}
if (wPropIDNext == 0) {
wPropIDNext = 0x8000; // start at 8000
} else {
wPropIDNext++; // next = one past current
}
}
return(PROP_TAG(PT_UNSPECIFIED, wPropIDNext++));
}
/** WAB specific GetIDsFromNames **/
HRESULT HrGetIDsFromNames(LPIAB lpIAB, ULONG cPropNames,
LPMAPINAMEID * lppPropNames, ULONG ulFlags, LPSPropTagArray * lppPropTags)
{
HRESULT hResult;
LPGUID_NAMED_PROPS lpgnp = NULL, lpgnpNew = NULL, lpgnpOld = NULL;
LPNAMED_PROP lpnm;
ULONG ulEntryCount;
ULONG i, j, k;
ULONG ulEntryCountOld = 0;
BOOL fChanged = FALSE;
LPTSTR lpName = NULL;
LPTSTR * lpID = NULL;
LPTSTR * rgNames = NULL;
ULONG ulNameSize;
UCHAR ucDefaultChar = '\002';
UCHAR ucNumericChar = '\001';
LPPROPERTY_STORE lpPropertyStore = lpIAB->lpPropertyStore;
LPPTGDATA lpPTGData=GetThreadStoragePointer();
if(pt_bIsWABOpenExSession)
{
// This is a WABOpenEx session using outlooks storage provider
if(!lpPropertyStore->hPropertyStore)
return MAPI_E_NOT_INITIALIZED;
{
LPWABSTORAGEPROVIDER lpWSP = (LPWABSTORAGEPROVIDER) lpPropertyStore->hPropertyStore;
hResult = lpWSP->lpVtbl->GetIDsFromNames( lpWSP,
cPropNames,
lppPropNames,
ulFlags,
lppPropTags);
DebugTrace(TEXT("WABStorageProvider::GetIDsFromNames returned:%x\n"),hResult);
return hResult;
}
}
*lppPropTags = NULL;
// Call into property store for the table of named props
if (hResult = GetNamedPropsFromPropStore(lpPropertyStore->hPropertyStore,
&ulEntryCountOld,
&lpgnpOld)) {
DebugTraceResult( TEXT("GetNamedPropsFromPropStore"), hResult);
goto exit;
}
ulEntryCount = ulEntryCountOld;
if (hResult = ResultFromScode(MAPIAllocateBuffer(sizeof(SPropTagArray) + (cPropNames * sizeof(ULONG)), lppPropTags))) {
DebugTraceResult( TEXT("GetIDsFromNames allocation of proptag array"), hResult);
goto exit;
}
(*lppPropTags)->cValues = cPropNames;
// If we're creating new entries, copy the existing array into a new one with space
// for worst-case expansion.
if (ulFlags & MAPI_CREATE) {
if (! (lpgnpNew = LocalAlloc(LPTR, (ulEntryCount + cPropNames) * sizeof(GUID_NAMED_PROPS)))) {
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
if (ulEntryCount) {
// Copy the existing array into the new one. Retain the same GUID pointers.
CopyMemory(lpgnpNew, lpgnpOld, ulEntryCount * sizeof(GUID_NAMED_PROPS));
// Now, copy the prop arrays for each GUID
for (i = 0; i < ulEntryCount; i++) {
if (! (lpnm = LocalAlloc(LPTR, (cPropNames + lpgnpNew[i].cValues) * sizeof(NAMED_PROP)))) {
LocalFreeAndNull(&lpgnpNew);
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
// Copy the existing array into the new one. Retain string pointers.
CopyMemory(lpnm, lpgnpOld[i].lpnm, lpgnpOld[i].cValues * sizeof(NAMED_PROP));
lpgnpNew[i].lpnm = lpnm;
}
}
lpgnp = lpgnpNew; // Use the new one
} else {
lpgnp = lpgnpOld; // Use the old one
}
// Allocate an array for ANSI name strings
if (! (rgNames = LocalAlloc(LPTR, cPropNames * sizeof(LPTSTR)))) {
DebugTrace(TEXT("GetIDsFromNames couldn't allocate names array\n"));
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
if (! (lpID = LocalAlloc(LPTR, cPropNames * sizeof(LPTSTR *)))) {
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
// For each requested property, look through the prop store values
for (i = 0; i < cPropNames; i++) {
if (lppPropNames[i]->ulKind == MNID_ID) {
// Map the numeric ID into a string name
DWORD cchSize = (MAX_DIGITS_ULONG_10 + 2);
if (! (rgNames[i] = LocalAlloc(LPTR, sizeof(TCHAR)*cchSize))) {
DebugTrace(TEXT("GetIDsFromNames couldn't allocate name buffer\n"));
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
wnsprintf(rgNames[i], cchSize, TEXT("%c%u"), ucNumericChar, lppPropNames[i]->Kind.lID);
lpName = rgNames[i];
}
else if (lppPropNames[i]->ulKind == MNID_STRING)
{
ulNameSize = lstrlen(lppPropNames[i]->Kind.lpwstrName)+1;
if (! ulNameSize) {
// invalid name
DebugTrace(TEXT("GetIDsFromNames WideCharToMultiByte -> %u\n"), GetLastError());
(*lppPropTags)->aulPropTag[i] = PROP_TAG(PT_ERROR, PR_NULL);
hResult = ResultFromScode(MAPI_W_ERRORS_RETURNED);
continue;
}
if (! (lpID[i] = LocalAlloc(LPTR, ulNameSize*sizeof(TCHAR)))) {
DebugTrace(TEXT("GetIDsFromNames couldn't allocate name buffer\n"));
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
StrCpyN(lpID[i],lppPropNames[i]->Kind.lpwstrName, ulNameSize);
lpName = lpID[i];
}
(*lppPropTags)->aulPropTag[i] = PR_NULL; // init to NULL
for (j = 0; j < ulEntryCount; j++) {
if (! memcmp(lppPropNames[i]->lpguid, lpgnp[j].lpGUID, sizeof(GUID))) {
for (k = 0; k < lpgnp[j].cValues; k++) {
if (! lstrcmpi(lpgnp[j].lpnm[k].lpsz, lpName)) {
// found it
(*lppPropTags)->aulPropTag[i] = lpgnp[j].lpnm[k].ulPropTag;
break;
}
}
if ((*lppPropTags)->aulPropTag[i] == PR_NULL) {
if (ulFlags & MAPI_CREATE) {
// Create a new one since it's not there
register ULONG cValues = lpgnp[j].cValues;
lpgnp[j].lpnm[cValues].lpsz = lpName;
lpgnp[j].lpnm[cValues].ulPropTag = GetNewPropTag(lpgnp, ulEntryCount);
(*lppPropTags)->aulPropTag[i] = lpgnp[j].lpnm[cValues].ulPropTag;
lpgnp[j].cValues++;
fChanged = TRUE;
} else {
// Error
(*lppPropTags)->aulPropTag[i] = PROP_TAG(PT_ERROR, PR_NULL);
hResult = ResultFromScode(MAPI_W_ERRORS_RETURNED);
}
}
break;
}
}
if ((*lppPropTags)->aulPropTag[i] == PR_NULL) {
if (ulFlags & MAPI_CREATE) {
register ULONG cValues = 0;
// Must add the new GUID
lpgnp[ulEntryCount].lpGUID = lppPropNames[i]->lpguid;
lpgnp[ulEntryCount].cValues = 0;
// conservative: Allocate room in case we need to put all of the
// requested prop names in here.
if (! (lpgnp[ulEntryCount].lpnm = LocalAlloc(LPTR, cPropNames * sizeof(NAMED_PROP)))) {
hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY);
goto exit;
}
// Now, create a new prop
lpgnp[ulEntryCount].lpnm[cValues].lpsz = lpName;
lpgnp[ulEntryCount].lpnm[cValues].ulPropTag = GetNewPropTag(lpgnp, ulEntryCount);
(*lppPropTags)->aulPropTag[i] = lpgnp[ulEntryCount].lpnm[cValues].ulPropTag;
lpgnp[ulEntryCount].cValues++;
ulEntryCount++; // new GUID
fChanged = TRUE;
} else {
// Error
(*lppPropTags)->aulPropTag[i] = PROP_TAG(PT_ERROR, PR_NULL);
hResult = ResultFromScode(MAPI_W_ERRORS_RETURNED);
}
}
}
if (ulFlags & MAPI_CREATE && fChanged) {
// Save the property mappings
if (hResult = SetNamedPropsToPropStore(lpPropertyStore->hPropertyStore,
ulEntryCount,
lpgnp)) {
DebugTraceResult( TEXT("SetNamedPropToPropStore"), hResult);
}
}
exit:
if (rgNames) {
for (i = 0; i < cPropNames; i++) {
LocalFreeAndNull(&(rgNames[i]));
LocalFreeAndNull(&(lpID[i]));
}
LocalFreeAndNull((LPVOID *)&rgNames);
}
if(lpID)
LocalFreeAndNull((LPVOID*)&lpID);
if (lpgnpOld) {
FreeGuidnamedprops(ulEntryCountOld, lpgnpOld);
}
if (lpgnpNew) { // not so simple, only free the arrays, not the strings, guids
for (i = 0; i < ulEntryCount; i++) {
LocalFreeAndNull(&(lpgnpNew[i].lpnm));
}
LocalFreeAndNull(&lpgnpNew);
}
if (HR_FAILED(hResult)) {
FreeBufferAndNull(lppPropTags); // yes, no &
}
return(hResult);
}
#ifdef OLD_STUFF
// Forward reference
HRESULT HrFixupTDN(LPADRLIST lpRecipList);
//---------------------------------------------------------------------------
// Name: HrPrepareRecips()
//
// Description:
// Internal function that does what IAB_PrepareRecips does but
// also supports a status flag so we know if we called down
// into a provider and really made some modifications.
//
// Parameters:
// Returns:
// Effects:
// Notes:
// Revision:
//---------------------------------------------------------------------------
STDMETHODIMP
HrPrepareRecips(LPIAB lpIAB, ULONG ulFlags, LPSPropTagArray lpPropTagArray,
LPADRLIST lpRecipList, ULONG * pulPrepRecipStatus)
{
HRESULT hr = hrSuccess;
SCODE sc;
BOOL fPrepRequired;
LPLSTPROVDATA lpABProvData;
BOOL fFixupTDN = FALSE;
ULONG iTag;
// Verify that we need to call provider's PrepareRecips
sc = ScVerifyPrepareRecips(lpIAB, ulFlags, lpPropTagArray, lpRecipList,
&fPrepRequired);
if (FAILED(sc))
{
DebugTrace(TEXT("Failure calling ScVerifyPrepareResult sc = %08X\n"), sc);
hr = ResultFromScode(sc);
goto exit;
}
// Recipient properties already prepared, we're out o' here
if (!fPrepRequired)
goto exit;
else
{
if (pulPrepRecipStatus)
{
*pulPrepRecipStatus |= PREPARE_RECIP_MOD_REQUIRED;
}
}
// First handle the one-offs...
hr = INT_PrepareRecips (lpIAB, ulFlags, lpPropTagArray, lpRecipList);
if (hr)
{
// Log it
DebugTraceResult(IAB_PrepareRecips, hr);
}
// Get the list of logged in ABProviders from the session and
// Iterate down the list
for (lpABProvData = lpIAB->pSession->lstAdrProv.lpProvData; lpABProvData;
lpABProvData=lpABProvData->lstNext)
{
// For each logged in session, have them fix up their entries
hr = ((LPABLOGON)(lpABProvData->lpProviderInfo))->lpVtbl->PrepareRecips(
(LPABLOGON)(lpABProvData->lpProviderInfo), ulFlags, lpPropTagArray,
lpRecipList);
#ifdef DEBUG
if (HR_FAILED(hr))
DebugTrace(TEXT("Failure in AB Provider <%s> calling PrepareRecips()\n"),
lpABProvData->lpInitData->lpszDLLName);
#endif
DebugTraceResult(IAB_PrepareRecips, hr);
}
// mask any provider errors
hr = hrSuccess;
//
// Ok, Check to see if PR_TRANSMITABLE_DISPLAY_NAME_A was asked for
// and if so, make sure each recipient has one.
//
if (lpPropTagArray)
{
for (iTag=0; !fFixupTDN && iTag<lpPropTagArray->cValues; iTag++)
fFixupTDN =
(lpPropTagArray->aulPropTag[iTag] == PR_TRANSMITABLE_DISPLAY_NAME_A);
if (fFixupTDN)
{
hr = HrFixupTDN(lpRecipList);
}
}
exit:
DebugTraceResult(HrPrepareRecips, hr);
return hr;
}
//
// HrFixupTDN - Fixup Transmitable Display Name
//
// For those entries that do not have PR_TRANSMITABLE_DISPLAY_NAME
// MAPI will generate it for them.
//
HRESULT
HrFixupTDN(LPADRLIST lpRecipList)
{
HRESULT hResult = hrSuccess;
SCODE sc = S_OK;
ULONG iRecip;
LPSPropValue lpspvTDN = NULL;
LPSPropValue lpspvDN = NULL;
for (iRecip = 0; iRecip < lpRecipList->cEntries; iRecip++)
{
LPSPropValue lpspvUser = lpRecipList->aEntries[iRecip].rgPropVals;
ULONG cValues = lpRecipList->aEntries[iRecip].cValues;
//$ This is where we default the value of PR_TRANSMITABLE_DISPLAY_NAME
//$ if was asked for.
//
//
lpspvTDN = PpropFindProp(lpspvUser,
cValues,
PROP_TAG(PT_ERROR, PROP_ID(PR_TRANSMITABLE_DISPLAY_NAME_A)));
lpspvDN = PpropFindProp(lpspvUser, cValues, PR_DISPLAY_NAME_A);
if (lpspvTDN && lpspvDN)
{
LPSTR lpszDN;
LPSTR lpszTDN;
lpszDN = lpspvDN->Value.lpszA;
//
// Check to see if the DN is already in the form 'name'.
//
if (*lpszDN == '\'' &&
*(lpszDN+lstrlen(lpszDN)-1) == '\'')
{
//
// Simply point lpspvT to lpspvDN
//
lpspvTDN->ulPropTag = PR_TRANSMITABLE_DISPLAY_NAME_A;
lpspvTDN->Value.lpszA = lpszDN;
} else
{
//
// We tic it ourselves and set it back in...
//
DWORD cchSize = (lstrlen(lpszDN)+3);
sc = MAPIAllocateMore(sizeof(TCHAR)*cchSize, lpspvUser, &lpszTDN);
if (sc)
{
DebugTrace(TEXT("HrFixupTDN out of memory\n"));
hResult = ResultFromScode(sc);
goto error;
}
*lpszTDN = '\'';
StrCpyN(&(lpszTDN[1]), lpszDN, cchSize-1);
StrCatBuff(lpszTDN, TEXT("\'"), cchSize);
lpspvTDN->ulPropTag = PR_TRANSMITABLE_DISPLAY_NAME_A;
lpspvTDN->Value.lpszA = lpszTDN;
}
}
}
error:
DebugTraceResult(HrFixupTDN, hResult);
return hResult;
}
#endif