/* * 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;icValues;i++) { if(lpProps->aulPropTag[i] == PR_SEARCH_KEY) { for(j=i;jcValues-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;ilpIAB->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;iulCreateFlags & 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;ipmbinOlk) { 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 = 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); }