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.
1783 lines
57 KiB
1783 lines
57 KiB
/*
|
|
* MailUser.C - mostly just a copy of WRAP.C
|
|
*
|
|
* Wrapper for mailuser and distlist objects.
|
|
*
|
|
* Copyright 1992 - 1996 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
*/
|
|
|
|
#include "_apipch.h"
|
|
|
|
extern OlkContInfo *FindContainer(LPIAB lpIAB, ULONG cbEID, LPENTRYID lpEID);
|
|
|
|
/*********************************************************************
|
|
*
|
|
* The actual Wrapped IMAPIProp methods
|
|
*
|
|
*/
|
|
|
|
|
|
//
|
|
// Wrapped IMAPIProp jump table is defined here...
|
|
// Try to use as much of IAB as possible.
|
|
//
|
|
|
|
MailUser_Vtbl vtblMAILUSER = {
|
|
VTABLE_FILL
|
|
MailUser_QueryInterface,
|
|
(MailUser_AddRef_METHOD *) WRAP_AddRef,
|
|
MailUser_Release,
|
|
(MailUser_GetLastError_METHOD *) IAB_GetLastError,
|
|
MailUser_SaveChanges,
|
|
MailUser_GetProps,
|
|
MailUser_GetPropList,
|
|
MailUser_OpenProperty,
|
|
MailUser_SetProps,
|
|
MailUser_DeleteProps,
|
|
MailUser_CopyTo,
|
|
MailUser_CopyProps,
|
|
MailUser_GetNamesFromIDs,
|
|
MailUser_GetIDsFromNames,
|
|
};
|
|
|
|
|
|
|
|
//
|
|
// Interfaces supported by this object
|
|
//
|
|
#define MailUser_cInterfaces 2
|
|
|
|
LPIID MailUser_LPIID[MailUser_cInterfaces] =
|
|
{
|
|
(LPIID)&IID_IMailUser,
|
|
(LPIID)&IID_IMAPIProp
|
|
};
|
|
|
|
//
|
|
// Interfaces supported by this object
|
|
//
|
|
#define DistList_cInterfaces 3
|
|
|
|
LPIID DistList_LPIID[DistList_cInterfaces] =
|
|
{
|
|
(LPIID)&IID_IDistList,
|
|
(LPIID)&IID_IMailUser,
|
|
(LPIID)&IID_IMAPIProp
|
|
};
|
|
|
|
HRESULT HrValidateMailUser(LPMailUser lpMailUser);
|
|
void MAILUSERFreeContextData(LPMailUser lpMailUser);
|
|
void MAILUSERAssociateContextData(LPMAILUSER lpMailUser, LPWABEXTDISPLAY lpWEC);
|
|
|
|
|
|
const TCHAR szMAPIPDL[] = TEXT("MAPIPDL");
|
|
|
|
extern BOOL bDNisByLN;
|
|
|
|
// --------
|
|
// IUnknown
|
|
|
|
STDMETHODIMP
|
|
MailUser_QueryInterface(LPMailUser lpMailUser,
|
|
REFIID lpiid,
|
|
LPVOID * lppNewObj)
|
|
{
|
|
ULONG iIID;
|
|
|
|
#ifdef PARAMETER_VALIDATION
|
|
|
|
// Check to see if it has a jump table
|
|
if (IsBadReadPtr(lpMailUser, sizeof(LPVOID))) {
|
|
// No jump table found
|
|
return(ResultFromScode(E_INVALIDARG));
|
|
}
|
|
|
|
// Check to see if the jump table has at least sizeof IUnknown
|
|
if (IsBadReadPtr(lpMailUser->lpVtbl, 3 * sizeof(LPVOID))) {
|
|
// Jump table not derived from IUnknown
|
|
return(ResultFromScode(E_INVALIDARG));
|
|
}
|
|
|
|
// Check to see that it's MailUser_QueryInterface
|
|
if (lpMailUser->lpVtbl->QueryInterface != MailUser_QueryInterface) {
|
|
// Not my jump table
|
|
return(ResultFromScode(E_INVALIDARG));
|
|
}
|
|
|
|
// Is there enough there for an interface ID?
|
|
|
|
if (IsBadReadPtr(lpiid, sizeof(IID))) {
|
|
DebugTraceSc(MailUser_QueryInterface, E_INVALIDARG);
|
|
return(ResultFromScode(E_INVALIDARG));
|
|
}
|
|
|
|
// Is there enough there for a new object?
|
|
if (IsBadWritePtr(lppNewObj, sizeof(LPMailUser))) {
|
|
DebugTraceSc(MailUser_QueryInterface, E_INVALIDARG);
|
|
return(ResultFromScode(E_INVALIDARG));
|
|
}
|
|
|
|
#endif // PARAMETER_VALIDATION
|
|
|
|
EnterCriticalSection(&lpMailUser->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; // GROSS! Jump into a for loop!
|
|
}
|
|
|
|
// Now look through all the iids associated with this object, see if any match
|
|
for(iIID = 0; iIID < lpMailUser->cIID; iIID++)
|
|
if (!memcmp(lpMailUser->rglpIID[iIID], lpiid, sizeof(IID))) {
|
|
goodiid:
|
|
//
|
|
// It's a match of interfaces, we support this one then...
|
|
//
|
|
++lpMailUser->lcInit;
|
|
*lppNewObj = lpMailUser;
|
|
|
|
LeaveCriticalSection(&lpMailUser->cs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// No interface we've heard of...
|
|
//
|
|
LeaveCriticalSection(&lpMailUser->cs);
|
|
|
|
*lppNewObj = NULL; // OLE requires NULLing out parm on failure
|
|
DebugTraceSc(MailUser_QueryInterface, E_NOINTERFACE);
|
|
return(ResultFromScode(E_NOINTERFACE));
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
MailUser_Release (LPMailUser lpMailUser)
|
|
{
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
//
|
|
// Make sure the object is valid.
|
|
//
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, Release, lpVtbl)) {
|
|
return(1);
|
|
}
|
|
#endif
|
|
|
|
EnterCriticalSection(&lpMailUser->cs);
|
|
|
|
--lpMailUser->lcInit;
|
|
|
|
if (lpMailUser->lcInit == 0) {
|
|
|
|
// Free any context-menu extension data associated with this mailuser
|
|
MAILUSERFreeContextData(lpMailUser);
|
|
|
|
UlRelease(lpMailUser->lpPropData);
|
|
|
|
//
|
|
// Need to free the object
|
|
//
|
|
|
|
LeaveCriticalSection(&lpMailUser->cs);
|
|
DeleteCriticalSection(&lpMailUser->cs);
|
|
FreeBufferAndNull(&lpMailUser);
|
|
return(0);
|
|
}
|
|
|
|
LeaveCriticalSection(&lpMailUser->cs);
|
|
return(lpMailUser->lcInit);
|
|
}
|
|
|
|
|
|
|
|
// IProperty
|
|
|
|
STDMETHODIMP
|
|
MailUser_SaveChanges(LPMailUser lpMailUser,
|
|
ULONG ulFlags)
|
|
{
|
|
HRESULT hr = hrSuccess;
|
|
ULONG ulcValues = 0;
|
|
LPSPropValue lpPropArray = NULL, lpspv = NULL, lpPropsOld = NULL, lpPropNew = NULL;
|
|
LPSPropTagArray lpProps = NULL;
|
|
SPropertyRestriction PropRes;
|
|
SPropValue Prop = {0};
|
|
ULONG i, j;
|
|
ULONG iDisplayName = NOT_FOUND;
|
|
ULONG iEmailAddress = NOT_FOUND;
|
|
ULONG iDuplicate = 0;
|
|
ULONG ulObjType = 0;
|
|
BOOL bNewRecord = FALSE;
|
|
BOOL fReplace = FALSE;
|
|
ULONG ulCount = 0, ulcProps = 0, ulcOld = 0, ulcNew;
|
|
LPSBinary rgsbEntryIDs = NULL;
|
|
SPropValue OneProp;
|
|
BOOL fNewEntry = TRUE;
|
|
BOOL fDuplicate = FALSE;
|
|
SCODE sc;
|
|
SBinary sbEID = {0};
|
|
LPSBinary lpsbEID = NULL;
|
|
FILETIME ftOldModTime = {0};
|
|
FILETIME ftCurrentModTime = {0};
|
|
BOOL bSwap = FALSE;
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
// Make sure the object is valid.
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, SaveChanges, lpVtbl)) {
|
|
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
|
|
}
|
|
#endif
|
|
|
|
#ifndef DONT_ADDREF_PROPSTORE
|
|
if ((FAILED(sc = OpenAddRefPropertyStore(NULL, lpMailUser->lpIAB->lpPropertyStore)))) {
|
|
hr = ResultFromScode(sc);
|
|
goto exitNotAddRefed;
|
|
}
|
|
#endif
|
|
//
|
|
//$REVIEW how do we handle the FORCE_SAVE flag ?
|
|
//
|
|
|
|
//
|
|
// check read write access ...
|
|
//
|
|
if (lpMailUser->ulObjAccess == IPROP_READONLY) {
|
|
// error - cant save changes
|
|
hr = MAPI_E_NO_ACCESS;
|
|
goto exit;
|
|
}
|
|
|
|
// If this is a One-Off, we cannot save changes
|
|
if (! lpMailUser->lpIAB->lpPropertyStore) {
|
|
hr = ResultFromScode(MAPI_E_NO_SUPPORT);
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the properties for this item
|
|
//
|
|
if (HR_FAILED(hr = HrValidateMailUser(lpMailUser))) {
|
|
goto exit;
|
|
}
|
|
|
|
// see if this entry has an old modification time .. we would check that time in case of a
|
|
// merge ..
|
|
{
|
|
LPSPropValue lpProp = NULL;
|
|
if(!HR_FAILED(HrGetOneProp((LPMAPIPROP)lpMailUser, PR_LAST_MODIFICATION_TIME, &lpProp)))
|
|
{
|
|
CopyMemory(&ftOldModTime, &(lpProp->Value.ft), sizeof(ftOldModTime));
|
|
MAPIFreeBuffer(lpProp);
|
|
}
|
|
}
|
|
|
|
// Put in the modification time
|
|
OneProp.ulPropTag = PR_LAST_MODIFICATION_TIME;
|
|
GetSystemTimeAsFileTime(&OneProp.Value.ft);
|
|
if(!ftOldModTime.dwLowDateTime && !ftOldModTime.dwHighDateTime)
|
|
CopyMemory(&ftOldModTime, &OneProp.Value.ft, sizeof(ftOldModTime)); // if we dont have a mod time, use NOW
|
|
|
|
if (HR_FAILED(hr = lpMailUser->lpVtbl->SetProps(
|
|
lpMailUser,
|
|
1, // cValues
|
|
&OneProp, // lpPropArray
|
|
NULL))) { // lppProblems
|
|
DebugTraceResult( TEXT("SetProps(PR_LAST_MODIFICATION_TIME)"), hr);
|
|
goto exit;
|
|
}
|
|
// BUGBUG: If SaveChanges fails after this, the PR_MODIFICATION_TIME on the
|
|
// open object will still be updated, even though it no longer matches
|
|
// the persistent copy of the object. I can live with this since it
|
|
// really simplifies the code.
|
|
|
|
// if this is a new entry and there is a folder parent for it,
|
|
// add the folder parent's entryid to this entry
|
|
// This will be persisted when we do the write record
|
|
// Once write record returns a valid entryid, we can update the folder to
|
|
// make this new item a part of it
|
|
if (fNewEntry && bIsWABSessionProfileAware(lpMailUser->lpIAB) &&
|
|
//bAreWABAPIProfileAware(lpMailUser->lpIAB) &&
|
|
lpMailUser->pmbinOlk&& lpMailUser->pmbinOlk->lpb && lpMailUser->pmbinOlk->cb)
|
|
{
|
|
AddFolderParentEIDToItem(lpMailUser->lpIAB,
|
|
lpMailUser->pmbinOlk->cb,
|
|
(LPENTRYID) lpMailUser->pmbinOlk->lpb,
|
|
(LPMAPIPROP)lpMailUser, 0, NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// We want a lpPropArray that contains everything but the PR_SEARCH_KEY
|
|
//
|
|
|
|
if (HR_FAILED(hr = lpMailUser->lpVtbl->GetPropList( lpMailUser, MAPI_UNICODE, &lpProps)))
|
|
goto exit;
|
|
|
|
if(lpProps)
|
|
{
|
|
for(i=0;i<lpProps->cValues;i++)
|
|
{
|
|
if(lpProps->aulPropTag[i] == PR_SEARCH_KEY)
|
|
{
|
|
for(j=i;j<lpProps->cValues-1;j++)
|
|
lpProps->aulPropTag[j] = lpProps->aulPropTag[j+1];
|
|
lpProps->cValues--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get a LPSPropValue array of all the properties pertaining to this
|
|
// entry
|
|
//
|
|
if (HR_FAILED(hr = lpMailUser->lpVtbl->GetProps(
|
|
lpMailUser,
|
|
lpProps, // LPSPropTagArray - NULL returns all
|
|
MAPI_UNICODE, // flags
|
|
&ulcValues,
|
|
&lpPropArray)))
|
|
{
|
|
// DebugPrintError(("GetProps -> %x\n", hr));
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Determine the entryid of this thing
|
|
//
|
|
for(i = 0; i < ulcValues; i++) {
|
|
if (lpPropArray[i].ulPropTag == PR_DISPLAY_NAME) {
|
|
// We use DisplayName as our uniqueness key for Strict or Loose match tests
|
|
iDisplayName = i;
|
|
}
|
|
|
|
if (lpPropArray[i].ulPropTag == PR_EMAIL_ADDRESS) {
|
|
// We use email address as our secondary uniqueness key for Strict match tests
|
|
iEmailAddress = i;
|
|
}
|
|
|
|
if (lpPropArray[i].ulPropTag == PR_ENTRYID) {
|
|
if ((lpPropArray[i].Value.bin.cb != 0) &&
|
|
(lpPropArray[i].Value.bin.lpb != NULL))
|
|
{
|
|
sbEID.cb = lpPropArray[i].Value.bin.cb;
|
|
sbEID.lpb = lpPropArray[i].Value.bin.lpb;
|
|
|
|
// If this is a One-Off, we cannot save changes
|
|
if(WAB_ONEOFF == IsWABEntryID(sbEID.cb,(LPENTRYID)sbEID.lpb,NULL,NULL,NULL, NULL, NULL))
|
|
{
|
|
hr = ResultFromScode(MAPI_E_NO_SUPPORT);
|
|
goto exit;
|
|
}
|
|
|
|
fNewEntry = FALSE;
|
|
continue;
|
|
} else {
|
|
lpPropArray[i].Value.bin.cb = 0;
|
|
}
|
|
}
|
|
|
|
if (lpPropArray[i].ulPropTag == PR_OBJECT_TYPE) {
|
|
switch(lpPropArray[i].Value.l) {
|
|
case MAPI_MAILUSER:
|
|
ulObjType = RECORD_CONTACT;
|
|
break;
|
|
case MAPI_DISTLIST:
|
|
ulObjType = RECORD_DISTLIST;
|
|
break;
|
|
case MAPI_ABCONT:
|
|
ulObjType = RECORD_CONTAINER;
|
|
break;
|
|
default:
|
|
// DebugPrintError(("Unknown Object Type: %d\n",lpPropArray[i].Value.l));
|
|
hr = ResultFromScode(MAPI_E_INVALID_OBJECT);
|
|
goto exit;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Assert(iDisplayName != NOT_FOUND);
|
|
|
|
if (fNewEntry && (lpMailUser->ulCreateFlags & (CREATE_CHECK_DUP_STRICT | CREATE_CHECK_DUP_LOOSE))) {
|
|
// Need to test DisplayName against current store... Use FindRecord.
|
|
// Only need to test if this is a new record.
|
|
|
|
PropRes.lpProp = &(lpPropArray[iDisplayName]);
|
|
PropRes.relop = RELOP_EQ;
|
|
PropRes.ulPropTag = PR_DISPLAY_NAME;
|
|
|
|
ulCount = 0; // find them all
|
|
|
|
// Search the property store
|
|
Assert(lpMailUser->lpIAB->lpPropertyStore->hPropertyStore);
|
|
if (HR_FAILED(hr = FindRecords(lpMailUser->lpIAB->lpPropertyStore->hPropertyStore,
|
|
lpMailUser->pmbinOlk, // pmbinFolder
|
|
0, // ulFlags
|
|
TRUE, // Always TRUE
|
|
&PropRes, // Propertyrestriction
|
|
&ulCount, // IN: number of matches to find, OUT: number found
|
|
&rgsbEntryIDs))) {
|
|
DebugTraceResult(FindRecords, hr);
|
|
goto exit;
|
|
}
|
|
|
|
if (ulCount) { // Was a match found?
|
|
fDuplicate = TRUE;
|
|
iDuplicate = 0;
|
|
|
|
if (lpMailUser->ulCreateFlags & CREATE_CHECK_DUP_STRICT && iEmailAddress != NOT_FOUND) {
|
|
// Check the primary email address too
|
|
fDuplicate = FALSE;
|
|
for (i = 0; i < ulCount && ! fDuplicate; i++) {
|
|
|
|
// Look at each entry until a match for email address is found
|
|
// Read the record
|
|
if (HR_FAILED(hr = ReadRecord(lpMailUser->lpIAB->lpPropertyStore->hPropertyStore,
|
|
&(rgsbEntryIDs[i]), // EntryID
|
|
0, // ulFlags
|
|
&ulcProps, // number of props returned
|
|
&lpspv))) { // properties returned
|
|
DebugTraceResult(MailUser_SaveChanges:ReadRecord, hr);
|
|
// ignore it and move on
|
|
continue;
|
|
}
|
|
|
|
if (ulcProps) {
|
|
Assert(lpspv);
|
|
if (lpspv) {
|
|
// Look for PR_EMAIL_ADDRESS
|
|
for (j = 0; j < ulcProps; j++) {
|
|
if (lpspv[j].ulPropTag == PR_EMAIL_ADDRESS) {
|
|
// Compare the two:
|
|
if (! lstrcmpi(lpspv[j].Value.LPSZ,
|
|
lpPropArray[iEmailAddress].Value.LPSZ)) {
|
|
fDuplicate = TRUE;
|
|
iDuplicate = i; // entry to delete on CREATE_REPLACE
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Free the prop array
|
|
ReadRecordFreePropArray( lpMailUser->lpIAB->lpPropertyStore->hPropertyStore,
|
|
ulcProps,
|
|
&lpspv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fDuplicate) {
|
|
// Depending on the flags, we should do something special here.
|
|
// Found a duplicate.
|
|
if (lpMailUser->ulCreateFlags & CREATE_REPLACE) {
|
|
fReplace = TRUE;
|
|
} else {
|
|
// Fail
|
|
DebugTrace(TEXT("SaveChanges found collision... failed\n"));
|
|
hr = ResultFromScode(MAPI_E_COLLISION);
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Write the record to the property store
|
|
//
|
|
|
|
if(sbEID.cb)
|
|
lpsbEID = &sbEID;
|
|
else if(fReplace)
|
|
{
|
|
lpsbEID = &(rgsbEntryIDs[iDuplicate]);
|
|
|
|
Prop.ulPropTag = PR_ENTRYID;
|
|
Prop.Value.bin = *lpsbEID;
|
|
|
|
if (lpMailUser->ulCreateFlags & CREATE_MERGE)
|
|
{
|
|
|
|
// We're now asking the user if he wants to replace - ideally we should just merge
|
|
// the new entry with the old entry giving priority to the new data over the old
|
|
// This way the user doesnt lose information already on the contact and it becomes
|
|
// much easier to update the information through vCards and LDAP etc
|
|
|
|
// TO do the merge, we get all the existing data on the contact
|
|
if (HR_FAILED(hr = ReadRecord(lpMailUser->lpIAB->lpPropertyStore->hPropertyStore,
|
|
&(rgsbEntryIDs[iDuplicate]),
|
|
0,
|
|
&ulcProps,
|
|
&lpspv)))
|
|
{
|
|
DebugTrace(TEXT("SaveChanges: ReadRecord Failed\n"));
|
|
goto exit;
|
|
}
|
|
|
|
for(i=0;i<ulcProps;i++)
|
|
{
|
|
if(lpspv[i].ulPropTag == PR_LAST_MODIFICATION_TIME)
|
|
{
|
|
CopyMemory(&ftCurrentModTime, &(lpspv[i].Value.ft), sizeof(ftCurrentModTime));
|
|
lpspv[i].ulPropTag = PR_NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
sc = ScMergePropValues( 1, &Prop, // these are added just to make sure the ScMerge wont fail
|
|
ulcProps, lpspv,
|
|
&ulcOld, &lpPropsOld);
|
|
|
|
ReadRecordFreePropArray( lpMailUser->lpIAB->lpPropertyStore->hPropertyStore,
|
|
ulcProps, &lpspv);
|
|
|
|
if (sc != S_OK)
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ulcOld = 1;
|
|
lpPropsOld = &Prop;
|
|
}
|
|
|
|
|
|
// Nullify any new PR_ENTRYID prop on the new prop set so that
|
|
// we retain the old EID
|
|
for(i=0;i<ulcValues;i++)
|
|
{
|
|
if(lpPropArray[i].ulPropTag == PR_ENTRYID)
|
|
{
|
|
lpPropArray[i].ulPropTag = PR_NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fReplace && lpMailUser->ulCreateFlags & CREATE_MERGE)
|
|
{
|
|
// Check the FileTimes to see who stomps on whom
|
|
if(CompareFileTime(&ftOldModTime, &ftCurrentModTime)<0) // current changes are later than original ones
|
|
{
|
|
// swap the 2 prop arrays
|
|
ULONG ulTemp = ulcValues;
|
|
LPSPropValue lpTemp =lpPropArray;
|
|
ulcValues = ulcOld; ulcOld = ulTemp;
|
|
lpPropArray = lpPropsOld; lpPropsOld = lpTemp;
|
|
bSwap = TRUE;
|
|
}
|
|
}
|
|
|
|
// Now merge the new props with the old props
|
|
sc = ScMergePropValues( ulcOld, lpPropsOld,
|
|
ulcValues, lpPropArray,
|
|
&ulcNew, &lpPropNew);
|
|
|
|
// undo a swap above so we can free memory properly
|
|
if(bSwap)
|
|
{
|
|
// swap the 2 prop arrays
|
|
ULONG ulTemp = ulcValues;
|
|
LPSPropValue lpTemp =lpPropArray;
|
|
ulcValues = ulcOld; ulcOld = ulTemp;
|
|
lpPropArray = lpPropsOld; lpPropsOld = lpTemp;
|
|
}
|
|
if (sc != S_OK)
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
MAPIFreeBuffer(lpPropArray);
|
|
lpPropArray = lpPropNew;
|
|
ulcValues = ulcNew;
|
|
lpPropNew = NULL;
|
|
ulcNew = 0;
|
|
}
|
|
|
|
Assert(lpMailUser->lpIAB->lpPropertyStore->hPropertyStore);
|
|
|
|
// One last thing to check is that if this is a new record, it shouldn't have a record key and
|
|
// instance key set on it and if it is an existing record, the record key and instance key should
|
|
// be identical to the entryid
|
|
{
|
|
ULONG iEntryID = NOT_FOUND;
|
|
ULONG iRecordKey = NOT_FOUND;
|
|
ULONG iInstanceKey = NOT_FOUND;
|
|
for(i=0;i<ulcValues;i++)
|
|
{
|
|
switch(lpPropArray[i].ulPropTag)
|
|
{
|
|
case PR_ENTRYID:
|
|
iEntryID = i;
|
|
break;
|
|
case PR_RECORD_KEY:
|
|
iRecordKey = i;
|
|
break;
|
|
case PR_INSTANCE_KEY:
|
|
iInstanceKey = i;
|
|
break;
|
|
}
|
|
}
|
|
if(iEntryID == NOT_FOUND || !lpPropArray[iEntryID].Value.bin.cb)
|
|
{
|
|
if(iRecordKey != NOT_FOUND)
|
|
lpPropArray[iRecordKey].ulPropTag = PR_NULL;
|
|
if(iInstanceKey != NOT_FOUND)
|
|
lpPropArray[iInstanceKey].ulPropTag = PR_NULL;
|
|
}
|
|
else
|
|
{
|
|
if(iRecordKey != NOT_FOUND)
|
|
{
|
|
lpPropArray[iRecordKey].Value.bin.cb = lpPropArray[iEntryID].Value.bin.cb;
|
|
lpPropArray[iRecordKey].Value.bin.lpb = lpPropArray[iEntryID].Value.bin.lpb;
|
|
}
|
|
if(iInstanceKey != NOT_FOUND)
|
|
{
|
|
lpPropArray[iInstanceKey].Value.bin.cb = lpPropArray[iEntryID].Value.bin.cb;
|
|
lpPropArray[iInstanceKey].Value.bin.lpb = lpPropArray[iEntryID].Value.bin.lpb;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Just to make sure we knock out all the PR_NULL properties,
|
|
// recreate a new version of the PropValueArray if any PR_NULL exist
|
|
for(i=0;i<ulcValues;i++)
|
|
{
|
|
if(lpPropArray[i].ulPropTag == PR_NULL)
|
|
{
|
|
ULONG ulcNew = 0;
|
|
LPSPropValue lpPropsNew = NULL;
|
|
SPropValue Prop = {0};
|
|
Prop.ulPropTag = PR_NULL;
|
|
if(!(sc = ScMergePropValues( 1, &Prop, // these are added just to make sure the ScMerge wont fail
|
|
ulcValues, lpPropArray,
|
|
&ulcNew, &lpPropsNew)))
|
|
{
|
|
if(lpPropArray)
|
|
FreeBufferAndNull(&lpPropArray);
|
|
ulcValues = ulcNew;
|
|
lpPropArray = lpPropsNew;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
{
|
|
OlkContInfo * polkci = NULL;
|
|
LPSBinary lpsbContEID = NULL;
|
|
|
|
if(lpMailUser->pmbinOlk)
|
|
{
|
|
polkci = FindContainer( lpMailUser->lpIAB, lpMailUser->pmbinOlk->cb, (LPENTRYID)lpMailUser->pmbinOlk->lpb);
|
|
if(polkci)
|
|
lpsbContEID = polkci->lpEntryID;
|
|
}
|
|
|
|
if (HR_FAILED(hr = WriteRecord( lpMailUser->lpIAB->lpPropertyStore->hPropertyStore,
|
|
lpsbContEID,
|
|
IN OUT &lpsbEID,
|
|
IN 0, //flags - reserved
|
|
IN ulObjType,
|
|
IN ulcValues,
|
|
IN lpPropArray)))
|
|
{
|
|
// DebugPrintError(("WriteRecord Failed: %x\n",hr));
|
|
|
|
//$REVIEW writerecord will tell us if MAPI_E_OBJECT_DELETED
|
|
// how to get MAPI_E_OBJECT_CHANGED or MAPI_E_OBJECT_DELETED ?
|
|
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// if sbEID.cb was 0, we now have a new entryid in the lpsbEID struct
|
|
if(!sbEID.cb && !fReplace)
|
|
{
|
|
sbEID.lpb = lpsbEID->lpb;
|
|
sbEID.cb = lpsbEID->cb;
|
|
}
|
|
|
|
// if this is a first time save of a new entry, then if profiles are enabled and this entry
|
|
// has a folder parent marked on it, add the new entryid to the folder parent ...
|
|
if(fNewEntry && bIsWABSessionProfileAware(lpMailUser->lpIAB) &&
|
|
//bAreWABAPIProfileAware(lpMailUser->lpIAB) &&
|
|
lpMailUser->pmbinOlk&& lpMailUser->pmbinOlk->lpb && lpMailUser->pmbinOlk->cb)
|
|
{
|
|
AddItemEIDToFolderParent(lpMailUser->lpIAB,
|
|
lpMailUser->pmbinOlk->cb,
|
|
(LPENTRYID)lpMailUser->pmbinOlk->lpb,
|
|
sbEID.cb,
|
|
(LPENTRYID)sbEID.lpb);
|
|
}
|
|
|
|
// If this is a first time save, set the local entryid prop.
|
|
if (fReplace || fNewEntry)
|
|
{
|
|
OneProp.ulPropTag = PR_ENTRYID;
|
|
OneProp.Value.bin = (fReplace ? *lpsbEID : sbEID);
|
|
|
|
// Use the low-level SetProps to avoid the PR_ENTRYID filter.
|
|
if (HR_FAILED(hr = lpMailUser->lpPropData->lpVtbl->SetProps(
|
|
lpMailUser->lpPropData,
|
|
1, // cValues
|
|
&OneProp, // lpPropArray
|
|
NULL))) // lppProblems
|
|
{
|
|
DebugTraceResult( TEXT("SetProps(PR_ENTRYID)"), hr);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (ulFlags & KEEP_OPEN_READWRITE) {
|
|
lpMailUser->ulObjAccess = IPROP_READWRITE;
|
|
} else {
|
|
//$REVIEW
|
|
// whether the flag was READONLY or there was no flag,
|
|
// we'll make the future access now READONLY
|
|
//
|
|
lpMailUser->ulObjAccess = IPROP_READONLY;
|
|
}
|
|
|
|
exit:
|
|
|
|
#ifndef DONT_ADDREF_PROPSTORE
|
|
ReleasePropertyStore(lpMailUser->lpIAB->lpPropertyStore);
|
|
exitNotAddRefed:
|
|
#endif
|
|
|
|
FreeEntryIDs(lpMailUser->lpIAB->lpPropertyStore->hPropertyStore,
|
|
ulCount,
|
|
rgsbEntryIDs);
|
|
|
|
FreeBufferAndNull(&lpPropArray);
|
|
FreeBufferAndNull(&lpProps);
|
|
FreeBufferAndNull(&lpPropNew);
|
|
if(lpMailUser->ulCreateFlags & CREATE_MERGE)
|
|
FreeBufferAndNull(&lpPropsOld);
|
|
|
|
if(lpsbEID != &sbEID && !fReplace)
|
|
FreeEntryIDs(lpMailUser->lpIAB->lpPropertyStore->hPropertyStore,
|
|
1,
|
|
lpsbEID);
|
|
|
|
if ((HR_FAILED(hr)) && (ulFlags & MAPI_DEFERRED_ERRORS)) {
|
|
//$REVIEW : this is a grossly trivial handling of MAPI_DEFERRED_ERRORS ..
|
|
// BUGBUG: In fact, it isn't handling the errors at all!
|
|
//
|
|
hr = hrSuccess;
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
MailUser_GetProps(LPMailUser lpMailUser,
|
|
LPSPropTagArray lpPropTagArray,
|
|
ULONG ulFlags,
|
|
ULONG * lpcValues,
|
|
LPSPropValue * lppPropArray)
|
|
{
|
|
#if !defined(NO_VALIDATION)
|
|
// Make sure the object is valid.
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, GetProps, lpVtbl)) {
|
|
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
|
|
}
|
|
#endif
|
|
|
|
return(lpMailUser->lpPropData->lpVtbl->GetProps(
|
|
lpMailUser->lpPropData,
|
|
lpPropTagArray,
|
|
ulFlags,
|
|
lpcValues,
|
|
lppPropArray));
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
MailUser_GetPropList(LPMailUser lpMailUser,
|
|
ULONG ulFlags,
|
|
LPSPropTagArray * lppPropTagArray)
|
|
{
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
/* Make sure the object is valid.
|
|
*/
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, GetPropList, lpVtbl))
|
|
{
|
|
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
}
|
|
#endif
|
|
|
|
return lpMailUser->lpPropData->lpVtbl->GetPropList(
|
|
lpMailUser->lpPropData,
|
|
ulFlags,
|
|
lppPropTagArray);
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
MailUser_OpenProperty(LPMailUser lpMailUser,
|
|
ULONG ulPropTag,
|
|
LPCIID lpiid,
|
|
ULONG ulInterfaceOptions,
|
|
ULONG ulFlags,
|
|
LPUNKNOWN * lppUnk)
|
|
{
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
/* Make sure the object is valid.
|
|
*/
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, OpenProperty, lpVtbl))
|
|
{
|
|
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
}
|
|
#endif
|
|
|
|
return lpMailUser->lpPropData->lpVtbl->OpenProperty(
|
|
lpMailUser->lpPropData,
|
|
ulPropTag,
|
|
lpiid,
|
|
ulInterfaceOptions,
|
|
ulFlags,
|
|
lppUnk);
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
MailUser_SetProps(LPMailUser lpMailUser,
|
|
ULONG cValues,
|
|
LPSPropValue lpPropArray,
|
|
LPSPropProblemArray * lppProblems)
|
|
{
|
|
ULONG i;
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
/* Make sure the object is valid.
|
|
*/
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, SetProps, lpVtbl))
|
|
{
|
|
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
}
|
|
#endif
|
|
|
|
if (lpMailUser->lpIAB->lpPropertyStore) {
|
|
// Filter out any READ-ONLY props.
|
|
// Only do this if this is a real, WAB entry. Others, like LDAP
|
|
// mailusers should be able to set any props they like.
|
|
for (i = 0; i < cValues; i++) {
|
|
switch (lpPropArray[i].ulPropTag) {
|
|
case PR_ENTRYID:
|
|
{
|
|
// double check that this is a local entryid before reseting
|
|
ULONG cb = lpPropArray[i].Value.bin.cb;
|
|
LPENTRYID lp = (LPENTRYID) lpPropArray[i].Value.bin.lpb;
|
|
BYTE bType = IsWABEntryID(cb,lp,NULL,NULL,NULL, NULL, NULL);
|
|
if(WAB_PAB == bType || WAB_PABSHARED == bType || !cb || !lp)
|
|
lpPropArray[i].ulPropTag = PR_NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(lpMailUser->lpPropData->lpVtbl->SetProps(
|
|
lpMailUser->lpPropData,
|
|
cValues,
|
|
lpPropArray,
|
|
lppProblems));
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
MailUser_DeleteProps(LPMailUser lpMailUser,
|
|
LPSPropTagArray lpPropTagArray,
|
|
LPSPropProblemArray * lppProblems)
|
|
{
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
/* Make sure the object is valid.
|
|
*/
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, DeleteProps, lpVtbl))
|
|
{
|
|
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
}
|
|
#endif
|
|
|
|
return lpMailUser->lpPropData->lpVtbl->DeleteProps(
|
|
lpMailUser->lpPropData,
|
|
lpPropTagArray,
|
|
lppProblems);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
MailUser_CopyTo(LPMailUser lpMailUser,
|
|
ULONG ciidExclude,
|
|
LPCIID rgiidExclude,
|
|
LPSPropTagArray lpExcludeProps,
|
|
ULONG_PTR ulUIParam,
|
|
LPMAPIPROGRESS lpProgress,
|
|
LPCIID lpInterface,
|
|
LPVOID lpDestObj,
|
|
ULONG ulFlags,
|
|
LPSPropProblemArray * lppProblems)
|
|
{
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
/* Make sure the object is valid.
|
|
*/
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, CopyTo, lpVtbl))
|
|
{
|
|
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
}
|
|
#endif
|
|
|
|
// Make sure we're not copying to ourselves
|
|
|
|
if ((LPVOID)lpMailUser == (LPVOID)lpDestObj)
|
|
{
|
|
DebugTrace(TEXT("OOP MailUser_CopyTo(): Copying to self is not supported\n"));
|
|
return ResultFromScode(MAPI_E_NO_ACCESS);
|
|
}
|
|
|
|
|
|
return lpMailUser->lpPropData->lpVtbl->CopyTo(
|
|
lpMailUser->lpPropData,
|
|
ciidExclude,
|
|
rgiidExclude,
|
|
lpExcludeProps,
|
|
ulUIParam,
|
|
lpProgress,
|
|
lpInterface,
|
|
lpDestObj,
|
|
ulFlags,
|
|
lppProblems);
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
MailUser_CopyProps(LPMailUser lpMailUser,
|
|
LPSPropTagArray lpIncludeProps,
|
|
ULONG_PTR ulUIParam,
|
|
LPMAPIPROGRESS lpProgress,
|
|
LPCIID lpInterface,
|
|
LPVOID lpDestObj,
|
|
ULONG ulFlags,
|
|
LPSPropProblemArray * lppProblems)
|
|
{
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
/* Make sure the object is valid.
|
|
*/
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, CopyProps, lpVtbl))
|
|
{
|
|
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
}
|
|
#endif
|
|
|
|
return lpMailUser->lpPropData->lpVtbl->CopyProps(
|
|
lpMailUser->lpPropData,
|
|
lpIncludeProps,
|
|
ulUIParam,
|
|
lpProgress,
|
|
lpInterface,
|
|
lpDestObj,
|
|
ulFlags,
|
|
lppProblems);
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
MailUser_GetNamesFromIDs(LPMailUser lpMailUser,
|
|
LPSPropTagArray * lppPropTags,
|
|
LPGUID lpPropSetGuid,
|
|
ULONG ulFlags,
|
|
ULONG * lpcPropNames,
|
|
LPMAPINAMEID ** lpppPropNames)
|
|
{
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
// Make sure the object is valid.
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, GetNamesFromIDs, lpVtbl)){
|
|
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
|
|
}
|
|
#endif
|
|
|
|
return lpMailUser->lpPropData->lpVtbl->GetNamesFromIDs(
|
|
lpMailUser->lpPropData,
|
|
lppPropTags,
|
|
lpPropSetGuid,
|
|
ulFlags,
|
|
lpcPropNames,
|
|
lpppPropNames);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : GetIDsFromNames
|
|
|
|
Purpose : Map names to property tags
|
|
|
|
Parameters: lpMAILUSER -> MAILUSER object
|
|
cPropNames
|
|
lppPropNames
|
|
ulFlags
|
|
lppPropTags
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
STDMETHODIMP
|
|
MailUser_GetIDsFromNames(LPMailUser lpMailUser,
|
|
ULONG cPropNames,
|
|
LPMAPINAMEID * lppPropNames,
|
|
ULONG ulFlags,
|
|
LPSPropTagArray * lppPropTags)
|
|
{
|
|
#if !defined(NO_VALIDATION)
|
|
// Make sure the object is valid.
|
|
if (BAD_STANDARD_OBJ(lpMailUser, MailUser_, GetIDsFromNames, lpVtbl)) {
|
|
return(ResultFromScode(MAPI_E_INVALID_PARAMETER));
|
|
}
|
|
#endif
|
|
return HrGetIDsFromNames(lpMailUser->lpIAB,
|
|
cPropNames,
|
|
lppPropNames, ulFlags, lppPropTags);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrSetMAILUSERAccess
|
|
|
|
Purpose : Sets access flags on a MAILUSER object
|
|
|
|
Parameters: lpMAILUSER -> MAILUSER object
|
|
ulOpenFlags = MAPI flags: MAPI_MODIFY | MAPI_BEST_ACCESS
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : Set the access flags on the MAILUSER.
|
|
|
|
***************************************************************************/
|
|
HRESULT HrSetMAILUSERAccess(LPMAILUSER lpMAILUSER,
|
|
ULONG ulFlags)
|
|
{
|
|
ULONG ulAccess = IPROP_READONLY;
|
|
LPMailUser lpMailUser = (LPMailUser)lpMAILUSER;
|
|
|
|
switch (ulFlags& (MAPI_MODIFY | MAPI_BEST_ACCESS)) {
|
|
case MAPI_MODIFY:
|
|
case MAPI_BEST_ACCESS:
|
|
ulAccess = IPROP_READWRITE;
|
|
break;
|
|
|
|
case 0:
|
|
break;
|
|
|
|
default:
|
|
Assert(FALSE);
|
|
}
|
|
|
|
return(lpMailUser->lpPropData->lpVtbl->HrSetObjAccess(lpMailUser->lpPropData, ulAccess));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrNewMAILUSER
|
|
|
|
Purpose : Creates a new MAILUSER object
|
|
|
|
Parameters: lpPropertyStore -> property store structure
|
|
pmbinOlk = <Outlook> container this entry lives in
|
|
If this is a WAB Container, then set the FOLDER_PARENT
|
|
prop on the MailUser with this entryid
|
|
ulType = type of mailuser to create: {MAPI_MAILUSER, MAPI_DISTLIST}
|
|
ulCreateFlags = CreateEntry flags
|
|
lppMAILUSER -> Returned MAILUSER object.
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : WAB EID format is MAPI_ENTRYID:
|
|
BYTE abFlags[4];
|
|
MAPIUID mapiuid; // = WABONEOFFEID
|
|
BYTE bData[]; // Contains BYTE type followed by type
|
|
// specific data:
|
|
// WAB_ONEOFF:
|
|
// szDisplayName, szAddrType and szAddress.
|
|
// the delimiter is the null between the strings.
|
|
//
|
|
***************************************************************************/
|
|
enum BaseProps{
|
|
propPR_OBJECT_TYPE = 0,
|
|
propPR_ENTRYID,
|
|
propPR_ADDRTYPE,
|
|
propMax
|
|
};
|
|
|
|
HRESULT HrNewMAILUSER(LPIAB lpIAB,
|
|
LPSBinary pmbinOlk,
|
|
ULONG ulType,
|
|
ULONG ulCreateFlags,
|
|
LPVOID *lppMAILUSER)
|
|
{
|
|
LPMailUser lpMailUser = NULL;
|
|
SCODE sc;
|
|
HRESULT hr = hrSuccess;
|
|
LPPROPDATA lpPropData = NULL;
|
|
SPropValue spv[propMax];
|
|
ULONG cProps;
|
|
|
|
|
|
//
|
|
// Allocate space for the MAILUSER structure
|
|
//
|
|
if (FAILED(sc = MAPIAllocateBuffer(sizeof(MailUser), (LPVOID *) &lpMailUser))) {
|
|
hr = ResultFromScode(sc);
|
|
goto err;
|
|
}
|
|
|
|
ZeroMemory(lpMailUser, sizeof(MailUser));
|
|
|
|
switch (ulType) {
|
|
case MAPI_MAILUSER:
|
|
lpMailUser->cIID = MailUser_cInterfaces;
|
|
lpMailUser->rglpIID = MailUser_LPIID;
|
|
break;
|
|
|
|
case MAPI_DISTLIST:
|
|
lpMailUser->cIID = DistList_cInterfaces;
|
|
lpMailUser->rglpIID = DistList_LPIID;
|
|
break;
|
|
|
|
default:
|
|
hr = ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
goto err;
|
|
}
|
|
lpMailUser->lpVtbl = &vtblMAILUSER;
|
|
|
|
lpMailUser->lcInit = 1; // Caller is a reference
|
|
|
|
lpMailUser->hLastError = hrSuccess;
|
|
lpMailUser->idsLastError = 0;
|
|
lpMailUser->lpszComponent = NULL;
|
|
lpMailUser->ulContext = 0;
|
|
lpMailUser->ulLowLevelError = 0;
|
|
lpMailUser->ulErrorFlags = 0;
|
|
lpMailUser->ulCreateFlags = ulCreateFlags;
|
|
lpMailUser->lpMAPIError = NULL;
|
|
lpMailUser->ulObjAccess = IPROP_READWRITE;
|
|
|
|
lpMailUser->lpEntryID = NULL;
|
|
|
|
lpMailUser->lpIAB = lpIAB;
|
|
|
|
if(pmbinOlk)
|
|
{
|
|
if (FAILED(sc = MAPIAllocateMore(sizeof(SBinary), lpMailUser, (LPVOID *) &(lpMailUser->pmbinOlk)))) {
|
|
hr = ResultFromScode(sc);
|
|
goto err;
|
|
}
|
|
if (FAILED(sc = MAPIAllocateMore(pmbinOlk->cb, lpMailUser, (LPVOID *) &(lpMailUser->pmbinOlk->lpb)))) {
|
|
hr = ResultFromScode(sc);
|
|
goto err;
|
|
}
|
|
lpMailUser->pmbinOlk->cb = pmbinOlk->cb;
|
|
CopyMemory(lpMailUser->pmbinOlk->lpb, pmbinOlk->lpb, pmbinOlk->cb);
|
|
}
|
|
|
|
//
|
|
// Create IPropData
|
|
//
|
|
if (FAILED(sc = CreateIProp((LPIID)&IID_IMAPIPropData,
|
|
(ALLOCATEBUFFER FAR *) MAPIAllocateBuffer,
|
|
(ALLOCATEMORE FAR *) MAPIAllocateMore,
|
|
MAPIFreeBuffer,
|
|
NULL,
|
|
&lpPropData))) {
|
|
hr = ResultFromScode(sc);
|
|
goto err;
|
|
}
|
|
// PR_OBJECT_TYPE
|
|
spv[propPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE;
|
|
spv[propPR_OBJECT_TYPE].Value.l = ulType;
|
|
|
|
spv[propPR_ENTRYID].ulPropTag = PR_ENTRYID;
|
|
spv[propPR_ENTRYID].Value.bin.lpb = NULL;
|
|
spv[propPR_ENTRYID].Value.bin.cb = 0;
|
|
|
|
cProps = 2;
|
|
|
|
if (ulType == MAPI_DISTLIST) {
|
|
cProps++;
|
|
Assert(cProps <= propMax);
|
|
spv[propPR_ADDRTYPE].ulPropTag = PR_ADDRTYPE;
|
|
spv[propPR_ADDRTYPE].Value.LPSZ = (LPTSTR)szMAPIPDL;; // All DL's have this addrtype
|
|
}
|
|
|
|
|
|
//
|
|
// Set the default properties
|
|
//
|
|
if (HR_FAILED(hr = lpPropData->lpVtbl->SetProps(lpPropData,
|
|
cProps,
|
|
spv,
|
|
NULL)))
|
|
{
|
|
goto err;
|
|
}
|
|
|
|
lpPropData->lpVtbl->HrSetObjAccess(lpPropData, IPROP_READWRITE);
|
|
|
|
lpMailUser->lpPropData = lpPropData;
|
|
|
|
// All we want to do is initialize the MailUsers critical section
|
|
|
|
InitializeCriticalSection(&lpMailUser->cs);
|
|
|
|
*lppMAILUSER = (LPVOID)lpMailUser;
|
|
|
|
return(hrSuccess);
|
|
|
|
err:
|
|
FreeBufferAndNull(&lpMailUser);
|
|
UlRelease(lpPropData);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : ParseDisplayName
|
|
|
|
Purpose : Parses the display name into first/last names
|
|
|
|
Parameters: lpDisplayName = input display name
|
|
lppFirstName -> in/out first name string
|
|
lppLastName -> in/out last name string
|
|
lpvRoot = Root object to AllocMore onto (or NULL to use LocalAlloc)
|
|
lppLocalFree -> out: if lpvRoot == NULL, this is the LocalAlloc'ed buffer
|
|
which must be LocalFree'd.
|
|
|
|
Returns : TRUE if changes were made
|
|
|
|
***************************************************************************/
|
|
BOOL ParseDisplayName( LPTSTR lpDisplayName,
|
|
LPTSTR * lppFirstName,
|
|
LPTSTR * lppLastName,
|
|
LPVOID lpvRoot,
|
|
LPVOID * lppLocalFree)
|
|
{
|
|
BOOL fChanged = FALSE;
|
|
|
|
if (lppLocalFree) {
|
|
*lppLocalFree = NULL;
|
|
}
|
|
//
|
|
// Handle the case where DisplayName exists, First and Last are missing
|
|
//
|
|
if (!(*lppFirstName && lstrlen(*lppFirstName)) &&
|
|
!(*lppLastName && lstrlen(*lppLastName)) &&
|
|
lpDisplayName)
|
|
{
|
|
ULONG nLen = 0;
|
|
BOOL bMatchFound = FALSE;
|
|
ULONG ulBracketCount = 0; //counts any brackets and puts them in last name
|
|
LPTSTR lpFirstName, lpLastName;
|
|
register TCHAR * pch;
|
|
LPTSTR lpBuffer = NULL;
|
|
|
|
nLen = (lstrlen(lpDisplayName)+1);
|
|
|
|
if (lpvRoot) {
|
|
if (AllocateBufferOrMore(sizeof(TCHAR)*nLen, lpvRoot, &lpBuffer)) {
|
|
DebugTrace(TEXT("ParseDisplayName can't allocate buffer\n"));
|
|
goto exit;
|
|
}
|
|
} else {
|
|
if (lppLocalFree) {
|
|
*lppLocalFree = lpBuffer = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*nLen);
|
|
}
|
|
}
|
|
if(!lpBuffer)
|
|
goto exit;
|
|
|
|
StrCpyN(lpBuffer, lpDisplayName, nLen);
|
|
|
|
//DebugTrace(TEXT("Parsing: <%s>\n"), lpDisplayName);
|
|
|
|
TrimSpaces(lpBuffer);
|
|
|
|
nLen = lstrlen(lpBuffer); // recount length
|
|
|
|
//
|
|
// Find the last space in the DisplayName string and assume that everything after it
|
|
// is the last name.
|
|
//
|
|
// We know that the string does not end with a space.
|
|
*lppFirstName = lpBuffer;
|
|
lpFirstName = *lppFirstName; // default
|
|
|
|
|
|
// If there is a comma or semicolon, assume that it is in the form
|
|
// LAST, FIRST and ignore spaces.
|
|
pch = lpBuffer;
|
|
while (pch && *pch) {
|
|
switch (*pch) {
|
|
case '(':
|
|
case '{':
|
|
case '<':
|
|
case '[':
|
|
ulBracketCount++;
|
|
break;
|
|
|
|
case ')':
|
|
case '}':
|
|
case '>':
|
|
case ']':
|
|
if (ulBracketCount) {
|
|
ulBracketCount--;
|
|
} else {
|
|
// No matching bracket, assume no spaces
|
|
goto loop_out;
|
|
}
|
|
break;
|
|
|
|
case ',':
|
|
case ';':
|
|
// Here's our break. (Assume first comma is it. Later commas
|
|
// are part of first name.)
|
|
if (! ulBracketCount) {
|
|
lpFirstName = CharNext(pch);
|
|
// get past any spaces
|
|
//while (*lpFirstName && IsSpace(lpFirstName)) {
|
|
// lpFirstName = CharNext(lpFirstName);
|
|
//}
|
|
lpLastName = lpBuffer;
|
|
*pch = '\0'; // Terminate lpLastName
|
|
TrimSpaces(lpFirstName);
|
|
TrimSpaces(lpLastName);
|
|
|
|
*lppFirstName = lpFirstName;
|
|
*lppLastName = lpLastName;
|
|
goto loop_out;
|
|
}
|
|
break;
|
|
}
|
|
pch = CharNext(pch);
|
|
}
|
|
|
|
|
|
// No comma or semi-colon, look for spaces.
|
|
if (bDNisByLN) {
|
|
pch = lpBuffer;
|
|
|
|
// Start at beginning of DN string, looking for space
|
|
while (pch && *pch && !fChanged) {
|
|
switch (*pch) {
|
|
case '(':
|
|
case '{':
|
|
case '<':
|
|
case '[':
|
|
ulBracketCount++;
|
|
break;
|
|
|
|
case ')':
|
|
case '}':
|
|
case '>':
|
|
case ']':
|
|
if (ulBracketCount) {
|
|
ulBracketCount--;
|
|
} else {
|
|
// No matching bracket, assume no spaces
|
|
goto loop_out;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Space?
|
|
if (IsSpace(pch)) {
|
|
if (! ulBracketCount) {
|
|
lpFirstName = CharNext(pch);
|
|
lpLastName = lpBuffer;
|
|
*pch = '\0'; // Terminate lpLastName
|
|
TrimSpaces(lpFirstName);
|
|
TrimSpaces(lpLastName);
|
|
|
|
*lppFirstName = lpFirstName;
|
|
*lppLastName = lpLastName;
|
|
goto loop_out;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
pch = CharNext(pch);
|
|
}
|
|
} else {
|
|
register TCHAR * pchLast;
|
|
// Point to NULL. This will add one iteration to the loop but is
|
|
// easy and less code than putting it to the previous DBCS char.
|
|
pch = lpBuffer + nLen;
|
|
|
|
while (pch >= lpBuffer && !fChanged) {
|
|
switch (*pch) {
|
|
case '(':
|
|
case '{':
|
|
case '<':
|
|
case '[':
|
|
if (ulBracketCount) {
|
|
ulBracketCount--;
|
|
} else {
|
|
// No matching bracket, assume no spaces
|
|
goto loop_out;
|
|
}
|
|
break;
|
|
|
|
case ')':
|
|
case '}':
|
|
case '>':
|
|
case ']':
|
|
ulBracketCount++;
|
|
break;
|
|
|
|
case ',':
|
|
// This probably means that we have last name first, fix it.
|
|
if (! ulBracketCount) {
|
|
lpFirstName = CharNext(pch);
|
|
lpLastName = lpBuffer;
|
|
*pch = '\0'; // Terminate lpFirstName
|
|
TrimSpaces(lpFirstName);
|
|
TrimSpaces(lpLastName);
|
|
|
|
*lppLastName = lpLastName;
|
|
*lppFirstName = lpFirstName;
|
|
goto loop_out;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Space?
|
|
if (IsSpace(pch)) {
|
|
if (! ulBracketCount)
|
|
{
|
|
// we dont want to break next to a bracket, we
|
|
// want to break at the space after the bracket ..
|
|
// so if the next char is a bracket, we ignore this stop ..
|
|
LPTSTR lpTemp = CharNext(pch);
|
|
if( *lpTemp != '(' &&
|
|
*lpTemp != '<' &&
|
|
*lpTemp != '[' &&
|
|
*lpTemp != '{' )
|
|
{
|
|
lpLastName = CharNext(pch);
|
|
*pch = '\0'; // Terminate lpFirstName
|
|
TrimSpaces(lpFirstName);
|
|
TrimSpaces(lpLastName);
|
|
|
|
*lppLastName = lpLastName;
|
|
goto loop_out;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
if ((pchLast = CharPrev(lpBuffer, pch)) == pch) {
|
|
pch = lpBuffer - 1; // terminate the loop
|
|
} else {
|
|
pch = pchLast;
|
|
}
|
|
}
|
|
}
|
|
|
|
loop_out:
|
|
|
|
// This will force a save operation on exiting so we
|
|
fChanged = TRUE; // dont have to do this again the next time ...
|
|
}
|
|
exit:
|
|
return(fChanged);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : FixDisplayName
|
|
|
|
Purpose : Creates a display name
|
|
IF there is no data to create the name with,
|
|
sets the name to Unknown
|
|
|
|
Parameters: lpFirstName -> in first name string
|
|
lpMiddleName -> in middle name string
|
|
lpLastName -> in last name string
|
|
lpCompanyName -> in company name string
|
|
lpNickName -> in NickName string
|
|
lppDisplayName = in/out display name
|
|
lpvRoot = Root object to AllocMore onto (or NULL to use MAPIAllocateBuffer)
|
|
|
|
Returns : TRUE if changes were made
|
|
|
|
Comment :
|
|
***************************************************************************/
|
|
BOOL FixDisplayName( LPTSTR lpFirstName,
|
|
LPTSTR lpMiddleName,
|
|
LPTSTR lpLastName,
|
|
LPTSTR lpCompanyName,
|
|
LPTSTR lpNickName,
|
|
LPTSTR * lppDisplayName,
|
|
LPVOID lpvRoot)
|
|
{
|
|
BOOL fChanged = FALSE;
|
|
LPTSTR lpDisplayName = *lppDisplayName;
|
|
LPTSTR lpszFormattedDisplayName = NULL;
|
|
ULONG nLen=0;
|
|
|
|
// First create the correct Display Name
|
|
if(!SetLocalizedDisplayName(lpFirstName,
|
|
lpMiddleName,
|
|
lpLastName,
|
|
lpCompanyName,
|
|
lpNickName,
|
|
NULL,
|
|
0, // 0 means return a allocated string
|
|
bDNisByLN,
|
|
NULL,
|
|
&lpszFormattedDisplayName))
|
|
{
|
|
DebugPrintError(( TEXT("SetLocalizedDisplayName failed\n")));
|
|
|
|
// if all the input strings were blank, then this is a special
|
|
// case of no names. here we set display name = TEXT("Unknown")
|
|
if(lpFirstName || lpMiddleName || lpLastName || lpCompanyName || lpNickName)
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if(!lpszFormattedDisplayName)
|
|
{
|
|
TCHAR szBuf[MAX_UI_STR];
|
|
DWORD cchSize = 0;
|
|
|
|
szBuf[0]='\0';
|
|
LoadString(hinstMapiX, idsUnknownDisplayName, szBuf, CharSizeOf(szBuf));
|
|
cchSize = (lstrlen(szBuf)+1);
|
|
|
|
lpszFormattedDisplayName = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)* cchSize);
|
|
if(!lpszFormattedDisplayName)
|
|
goto exit;
|
|
StrCpyN(lpszFormattedDisplayName, szBuf, cchSize);
|
|
}
|
|
|
|
if(lpszFormattedDisplayName) {
|
|
DWORD cchSize = (lstrlen(lpszFormattedDisplayName) + 1);
|
|
|
|
if (AllocateBufferOrMore(sizeof(TCHAR)* cchSize, lpvRoot, lppDisplayName)) {
|
|
DebugTrace(TEXT("FixDisplayName can't allocate buffer\n"));
|
|
goto exit;
|
|
}
|
|
StrCpyN(*lppDisplayName, lpszFormattedDisplayName, cchSize);
|
|
|
|
fChanged = TRUE;
|
|
}
|
|
|
|
exit:
|
|
LocalFreeAndNull(&lpszFormattedDisplayName);
|
|
|
|
return(fChanged);
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrValidateMailUser
|
|
|
|
Purpose : Validates the properties of a MailUser object
|
|
|
|
Parameters: lpMailUser -> mailuser object
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
***************************************************************************/
|
|
HRESULT HrValidateMailUser(LPMailUser lpMailUser) {
|
|
HRESULT hResult = hrSuccess;
|
|
ULONG ulcValues, i;
|
|
LPSPropValue lpspv = NULL;
|
|
LPTSTR lpADDRTYPE = NULL;
|
|
BOOL fChanged = FALSE, fDL = FALSE;
|
|
LPTSTR lpGivenName, lpSurname, lpMiddleName, lpCompanyName, lpNickName, lpDisplayName;
|
|
|
|
|
|
|
|
// Get the interesting properties
|
|
if (HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(
|
|
lpMailUser,
|
|
(LPSPropTagArray)&tagaValidate,
|
|
MAPI_UNICODE, // flags
|
|
&ulcValues,
|
|
&lpspv))) {
|
|
DebugTraceResult( TEXT("HrValidateMailUser:GetProps"), hResult);
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// If there is a PR_ADDRTYPE, there must be a PR_EMAIL_ADDRESS
|
|
if (! PROP_ERROR(lpspv[ivPR_ADDRTYPE])) {
|
|
if (! lstrcmpi(lpspv[ivPR_ADDRTYPE].Value.LPSZ, szMAPIPDL)) {
|
|
fDL = TRUE;
|
|
} else {
|
|
if (PROP_ERROR(lpspv[ivPR_EMAIL_ADDRESS])) {
|
|
hResult = ResultFromScode(MAPI_E_MISSING_REQUIRED_COLUMN);
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! fDL) {
|
|
// Deal with name properties (not for DLs)
|
|
if (PROP_ERROR(lpspv[ivPR_SURNAME])) {
|
|
lpSurname = NULL;
|
|
} else {
|
|
lpSurname = lpspv[ivPR_SURNAME].Value.LPSZ;
|
|
}
|
|
|
|
if (PROP_ERROR(lpspv[ivPR_GIVEN_NAME])) {
|
|
lpGivenName = NULL;
|
|
} else {
|
|
lpGivenName = lpspv[ivPR_GIVEN_NAME].Value.LPSZ;
|
|
}
|
|
|
|
if (PROP_ERROR(lpspv[ivPR_MIDDLE_NAME])) {
|
|
lpMiddleName = NULL;
|
|
} else {
|
|
lpMiddleName = lpspv[ivPR_MIDDLE_NAME].Value.LPSZ;
|
|
}
|
|
|
|
if (PROP_ERROR(lpspv[ivPR_COMPANY_NAME])) {
|
|
lpCompanyName = NULL;
|
|
} else {
|
|
lpCompanyName = lpspv[ivPR_COMPANY_NAME].Value.LPSZ;
|
|
}
|
|
|
|
if (PROP_ERROR(lpspv[ivPR_NICKNAME])) {
|
|
lpNickName = NULL;
|
|
} else {
|
|
lpNickName = lpspv[ivPR_NICKNAME].Value.LPSZ;
|
|
}
|
|
|
|
if (PROP_ERROR(lpspv[ivPR_DISPLAY_NAME])) {
|
|
lpDisplayName = NULL;
|
|
} else {
|
|
lpDisplayName = lpspv[ivPR_DISPLAY_NAME].Value.LPSZ;
|
|
}
|
|
|
|
|
|
// WAB needs a display name otherwise it cannot handle the contact.
|
|
|
|
if(!lpDisplayName)
|
|
{
|
|
fChanged |= FixDisplayName( lpGivenName,
|
|
lpMiddleName,
|
|
lpSurname,
|
|
lpCompanyName,
|
|
lpNickName,
|
|
(LPTSTR *) (&lpspv[ivPR_DISPLAY_NAME].Value.LPSZ),
|
|
lpspv);
|
|
}
|
|
|
|
if (fChanged) {
|
|
lpspv[ivPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME;
|
|
}
|
|
}
|
|
|
|
// Must have a display name and an object type
|
|
if (PROP_ERROR(lpspv[ivPR_DISPLAY_NAME]) || PROP_ERROR(lpspv[ivPR_OBJECT_TYPE]) ||
|
|
lstrlen(lpspv[ivPR_DISPLAY_NAME].Value.LPSZ) == 0) {
|
|
hResult = ResultFromScode(MAPI_E_MISSING_REQUIRED_COLUMN);
|
|
goto exit;
|
|
}
|
|
|
|
// If there is a PR_CONTACT_ADDRTYPES there must be a PR_CONTACT_EMAIL_ADDRESSES
|
|
if (! PROP_ERROR(lpspv[ivPR_CONTACT_ADDRTYPES]) && PROP_ERROR(lpspv[ivPR_CONTACT_EMAIL_ADDRESSES])) {
|
|
hResult = ResultFromScode(MAPI_E_MISSING_REQUIRED_COLUMN);
|
|
goto exit;
|
|
}
|
|
|
|
// Save changes
|
|
if (fChanged) {
|
|
// Null out any error values
|
|
for (i = 0; i < ulcValues; i++) {
|
|
if (PROP_ERROR(lpspv[i])) {
|
|
lpspv[i].ulPropTag = PR_NULL;
|
|
}
|
|
}
|
|
|
|
if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser,
|
|
ulcValues,
|
|
lpspv,
|
|
NULL))) {
|
|
DebugTraceResult( TEXT("HrValidateMailUser:SetProps"), hResult);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
FreeBufferAndNull(&lpspv);
|
|
|
|
return(hResult);
|
|
}
|
|
|
|
|
|
//$$
|
|
/*
|
|
- MAILUSERAssociateContextData
|
|
-
|
|
* With Context Menu extensions, we pass data to other apps and other apps
|
|
* need this data to exsist as long as the corresponding MailUser exists
|
|
*/
|
|
void MAILUSERAssociateContextData(LPMAILUSER lpMailUser, LPWABEXTDISPLAY lpWEC)
|
|
{
|
|
((LPMailUser)lpMailUser)->lpv = (LPVOID) lpWEC;
|
|
}
|
|
|
|
//$$
|
|
/*
|
|
- MAILUSERFreeContextData
|
|
-
|
|
* If Context Menu data was associated with this MailUSer, its time to clean it up
|
|
*/
|
|
void MAILUSERFreeContextData(LPMailUser lpMailUser)
|
|
{
|
|
LPWABEXTDISPLAY lpWEC = (LPWABEXTDISPLAY) lpMailUser->lpv;
|
|
if(!lpWEC)
|
|
return;
|
|
if(lpWEC->lpv)
|
|
FreePadrlist((LPADRLIST)lpWEC->lpv);
|
|
if(lpWEC->lpsz)
|
|
LocalFree(lpWEC->lpsz);
|
|
LocalFree(lpWEC);
|
|
}
|