123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <new>
- #include <kopano/platform.h>
- #include <kopano/ECInterfaceDefs.h>
- #include <kopano/memory.hpp>
- #include "ECExchangeImportHierarchyChanges.h"
- #include "ECExchangeImportContentsChanges.h"
- #include <kopano/Util.h>
- #include <kopano/ECGuid.h>
- #include <edkguid.h>
- #include <mapiguid.h>
- #include <kopano/mapiext.h>
- #include <kopano/ECDebug.h>
- #include <kopano/stringutil.h>
- #include "pcutil.hpp"
- #include "ics.h"
- #include <mapiutil.h>
- #include "Mem.h"
- #include <kopano/mapi_ptr.h>
- #include "EntryPoint.h"
- #include <kopano/charset/convert.h>
- #include <kopano/charset/utf8string.h>
- #include <kopano/charset/convstring.h>
- using namespace KCHL;
- ECExchangeImportHierarchyChanges::ECExchangeImportHierarchyChanges(ECMAPIFolder *lpFolder) :
- m_lpFolder(lpFolder)
- {
- m_lpFolder->AddRef();
- }
- ECExchangeImportHierarchyChanges::~ECExchangeImportHierarchyChanges(){
- m_lpFolder->Release();
- }
- HRESULT ECExchangeImportHierarchyChanges::Create(ECMAPIFolder *lpFolder, LPEXCHANGEIMPORTHIERARCHYCHANGES* lppExchangeImportHierarchyChanges){
- if(!lpFolder)
- return MAPI_E_INVALID_PARAMETER;
- auto lpEIHC = new(std::nothrow) ECExchangeImportHierarchyChanges(lpFolder);
- if (lpEIHC == nullptr)
- return MAPI_E_NOT_ENOUGH_MEMORY;
- auto ret = lpEIHC->QueryInterface(IID_IExchangeImportHierarchyChanges,
- reinterpret_cast<void **>(lppExchangeImportHierarchyChanges));
- if (ret != hrSuccess)
- delete lpEIHC;
- return ret;
- }
- HRESULT ECExchangeImportHierarchyChanges::QueryInterface(REFIID refiid, void **lppInterface)
- {
- REGISTER_INTERFACE2(ECExchangeImportHierarchyChanges, this);
- REGISTER_INTERFACE2(ECUnknown, this);
- REGISTER_INTERFACE2(IExchangeImportHierarchyChanges, &this->m_xExchangeImportHierarchyChanges);
- REGISTER_INTERFACE2(IUnknown, &this->m_xExchangeImportHierarchyChanges);
- return MAPI_E_INTERFACE_NOT_SUPPORTED;
- }
- HRESULT ECExchangeImportHierarchyChanges::GetLastError(HRESULT hResult, ULONG ulFlags, LPMAPIERROR *lppMAPIError){
- HRESULT hr = hrSuccess;
- ecmem_ptr<MAPIERROR> lpMapiError;
- memory_ptr<TCHAR> lpszErrorMsg;
-
- //FIXME: give synchronization errors messages
- hr = Util::HrMAPIErrorToText((hResult == hrSuccess)?MAPI_E_NO_ACCESS : hResult, &~lpszErrorMsg);
- if (hr != hrSuccess)
- return hr;
- hr = ECAllocateBuffer(sizeof(MAPIERROR), &~lpMapiError);
- if(hr != hrSuccess)
- return hr;
- if (ulFlags & MAPI_UNICODE) {
- std::wstring wstrErrorMsg = convert_to<std::wstring>(lpszErrorMsg.get());
- std::wstring wstrCompName = convert_to<std::wstring>(g_strProductName.c_str());
- if ((hr = MAPIAllocateMore(sizeof(std::wstring::value_type) * (wstrErrorMsg.size() + 1), lpMapiError, (void**)&lpMapiError->lpszError)) != hrSuccess)
- return hr;
- wcscpy((wchar_t*)lpMapiError->lpszError, wstrErrorMsg.c_str());
- if ((hr = MAPIAllocateMore(sizeof(std::wstring::value_type) * (wstrCompName.size() + 1), lpMapiError, (void**)&lpMapiError->lpszComponent)) != hrSuccess)
- return hr;
- wcscpy((wchar_t*)lpMapiError->lpszComponent, wstrCompName.c_str());
- } else {
- std::string strErrorMsg = convert_to<std::string>(lpszErrorMsg.get());
- std::string strCompName = convert_to<std::string>(g_strProductName.c_str());
- if ((hr = MAPIAllocateMore(strErrorMsg.size() + 1, lpMapiError, (void**)&lpMapiError->lpszError)) != hrSuccess)
- return hr;
- strcpy((char*)lpMapiError->lpszError, strErrorMsg.c_str());
- if ((hr = MAPIAllocateMore(strCompName.size() + 1, lpMapiError, (void**)&lpMapiError->lpszComponent)) != hrSuccess)
- return hr;
- strcpy((char*)lpMapiError->lpszComponent, strCompName.c_str());
- }
- lpMapiError->ulContext = 0;
- lpMapiError->ulLowLevelError= 0;
- lpMapiError->ulVersion = 0;
- *lppMAPIError = lpMapiError.release();
- return hrSuccess;
- }
- HRESULT ECExchangeImportHierarchyChanges::Config(LPSTREAM lpStream, ULONG ulFlags){
- HRESULT hr = hrSuccess;
- LARGE_INTEGER zero = {{0,0}};
- ULONG ulLen = 0;
- memory_ptr<SPropValue> lpPropSourceKey;
- m_lpStream = lpStream;
- if(lpStream == NULL) {
- m_ulSyncId = 0;
- m_ulChangeId = 0;
- } else {
- hr = lpStream->Seek(zero, STREAM_SEEK_SET, NULL);
- if(hr != hrSuccess)
- return hr;
- hr = lpStream->Read(&m_ulSyncId, 4, &ulLen);
- if(hr != hrSuccess)
- return hr;
- if(ulLen != 4)
- return MAPI_E_INVALID_PARAMETER;
- hr = lpStream->Read(&m_ulChangeId, 4, &ulLen);
- if(hr != hrSuccess)
- return hr;
- if(ulLen != 4) {
- return MAPI_E_INVALID_PARAMETER;
- }
- hr = HrGetOneProp(&m_lpFolder->m_xMAPIFolder, PR_SOURCE_KEY, &~lpPropSourceKey);
- if(hr != hrSuccess)
- return hr;
- // The user specified the special sync key '0000000000000000', get a sync key from the server.
- if(m_ulSyncId == 0) {
- hr = m_lpFolder->GetMsgStore()->lpTransport->HrSetSyncStatus(std::string((char *)lpPropSourceKey->Value.bin.lpb, lpPropSourceKey->Value.bin.cb), m_ulSyncId, m_ulChangeId, ICS_SYNC_HIERARCHY, 0, &m_ulSyncId);
- if(hr != hrSuccess)
- return hr;
- }
- // The sync key we got from the server can be used to retrieve all items in the database now when given to IEEC->Config(). At the same time, any
- // items written to this importer will send the sync ID to the server so that any items written here will not be returned by the exporter,
- // preventing local looping of items.
- }
- m_ulFlags = ulFlags;
- return hrSuccess;
- }
- //write into the stream 4 bytes syncid and 4 bytes changeid
- HRESULT ECExchangeImportHierarchyChanges::UpdateState(LPSTREAM lpStream){
- HRESULT hr;
- LARGE_INTEGER zero = {{0,0}};
- ULONG ulLen = 0;
-
- if(lpStream == NULL) {
- if (m_lpStream == NULL)
- return hrSuccess;
- lpStream = m_lpStream;
- }
- if(m_ulSyncId == 0)
- return hrSuccess; // config() called with NULL stream, so we'll ignore the UpdateState()
-
- hr = lpStream->Seek(zero, STREAM_SEEK_SET, NULL);
- if(hr != hrSuccess)
- return hr;
-
- hr = lpStream->Write(&m_ulSyncId, 4, &ulLen);
- if(hr != hrSuccess)
- return hr;
- if (m_ulSyncId == 0)
- m_ulChangeId = 0;
- return lpStream->Write(&m_ulChangeId, 4, &ulLen);
- }
- HRESULT ECExchangeImportHierarchyChanges::ImportFolderChange(ULONG cValue, LPSPropValue lpPropArray){
- HRESULT hr = hrSuccess;
- ////The array must contain at least the PR_PARENT_SOURCE_KEY, PR_SOURCE_KEY, PR_CHANGE_KEY, PR_PREDECESSOR_CHANGE_LIST, and MAPI PR_DISPLAY_NAME properties.
- auto lpPropParentSourceKey = PCpropFindProp(lpPropArray, cValue, PR_PARENT_SOURCE_KEY);
- auto lpPropSourceKey = PCpropFindProp(lpPropArray, cValue, PR_SOURCE_KEY);
- auto lpPropDisplayName = PCpropFindProp(lpPropArray, cValue, PR_DISPLAY_NAME);
- auto lpPropComment = PCpropFindProp(lpPropArray, cValue, PR_COMMENT);
- auto lpPropChangeKey = PCpropFindProp(lpPropArray, cValue, PR_CHANGE_KEY);
- auto lpPropFolderType = PCpropFindProp(lpPropArray, cValue, PR_FOLDER_TYPE);
- auto lpPropChangeList = PCpropFindProp(lpPropArray, cValue, PR_PREDECESSOR_CHANGE_LIST);
- auto lpPropEntryId = PCpropFindProp(lpPropArray, cValue, PR_ENTRYID);
- auto lpPropAdditionalREN = PCpropFindProp(lpPropArray, cValue, PR_ADDITIONAL_REN_ENTRYIDS);
- memory_ptr<SPropValue> lpPropVal;
- memory_ptr<ENTRYID> lpEntryId, lpDestEntryId;
- ULONG cbEntryId;
- ULONG cbDestEntryId;
- ULONG ulObjType;
- object_ptr<IMAPIFolder> lpFolder, lpParentFolder;
- object_ptr<ECMAPIFolder> lpECFolder, lpECParentFolder;
- ULONG ulFolderType = FOLDER_GENERIC;
- utf8string strFolderComment;
- ULONG cbOrigEntryId = 0;
- BYTE *lpOrigEntryId = NULL;
- const SBinary *lpOrigSourceKey = NULL;
- std::string strChangeList;
- ULONG ulPos = 0;
- ULONG ulSize = 0;
- bool bConflict = false;
- if (lpPropParentSourceKey == nullptr || lpPropSourceKey == nullptr ||
- lpPropDisplayName == nullptr)
- return MAPI_E_CALL_FAILED;
- if (lpPropComment)
- strFolderComment = convert_to<utf8string>(lpPropComment->Value.lpszW);
- if (lpPropEntryId && IsKopanoEntryId(lpPropEntryId->Value.bin.cb, lpPropEntryId->Value.bin.lpb)) {
- cbOrigEntryId = lpPropEntryId->Value.bin.cb;
- lpOrigEntryId = lpPropEntryId->Value.bin.lpb;
- }
- if (lpPropSourceKey != nullptr)
- lpOrigSourceKey = &lpPropSourceKey->Value.bin;
- if (lpPropFolderType != nullptr)
- ulFolderType = lpPropFolderType->Value.ul;
- if (ulFolderType == FOLDER_SEARCH)
- //ignore search folder
- return hrSuccess;
- hr = m_lpFolder->GetMsgStore()->lpTransport->HrEntryIDFromSourceKey(m_lpFolder->GetMsgStore()->m_cbEntryId, m_lpFolder->GetMsgStore()->m_lpEntryId, lpPropSourceKey->Value.bin.cb, lpPropSourceKey->Value.bin.lpb, 0, NULL, &cbEntryId, &~lpEntryId);
- if(hr == MAPI_E_NOT_FOUND){
- // Folder is not yet available in our store
- if(lpPropParentSourceKey->Value.bin.cb > 0){
- // Find the parent folder in which the new folder is to be created
- hr = m_lpFolder->GetMsgStore()->lpTransport->HrEntryIDFromSourceKey(m_lpFolder->GetMsgStore()->m_cbEntryId, m_lpFolder->GetMsgStore()->m_lpEntryId , lpPropParentSourceKey->Value.bin.cb, lpPropParentSourceKey->Value.bin.lpb, 0, NULL, &cbEntryId, &~lpEntryId);
- if(hr != hrSuccess)
- return hr;
- if (cbEntryId == 0)
- return MAPI_E_CALL_FAILED;
- hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpParentFolder);
- if(hr != hrSuccess)
- return hr;
- hr = lpParentFolder->QueryInterface(IID_ECMAPIFolder, &~lpECParentFolder);
- if(hr != hrSuccess)
- return hr;
-
- // Create the folder, loop through some names if it collides
- hr = lpECParentFolder->lpFolderOps->HrCreateFolder(ulFolderType, convstring(lpPropDisplayName->Value.lpszW), strFolderComment, 0, m_ulSyncId, lpOrigSourceKey, cbOrigEntryId, (LPENTRYID)lpOrigEntryId, &cbEntryId, &~lpEntryId);
- if(hr != hrSuccess)
- return hr;
- }else{
- hr = m_lpFolder->lpFolderOps->HrCreateFolder(ulFolderType, convstring(lpPropDisplayName->Value.lpszW), strFolderComment, 0, m_ulSyncId, lpOrigSourceKey, cbOrigEntryId, (LPENTRYID)lpOrigEntryId, &cbEntryId, &~lpEntryId);
- if (hr != hrSuccess)
- return hr;
- }
- // Open the folder we just created
- hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpFolder);
- if(hr != hrSuccess)
- return hr;
- }else if(hr != hrSuccess){
- return hr;
- }else if(cbEntryId == 0){
- return MAPI_E_CALL_FAILED;
- }else if(cbEntryId == m_lpFolder->m_cbEntryId && memcmp(lpEntryId, m_lpFolder->m_lpEntryId, cbEntryId)==0){
- // We are the changed folder
- hr = m_lpFolder->QueryInterface(IID_IMAPIFolder, &~lpFolder);
- if(hr != hrSuccess)
- return hr;
- }else{
- bool bRestored = false;
- // Changed folder is an existing subfolder
- hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpFolder);
- if(hr != hrSuccess){
- hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY | SHOW_SOFT_DELETES, &ulObjType, &~lpFolder);
- if(hr != hrSuccess)
- return hr;
- /**
- * If the folder was deleted locally, it must have been resotored remote in order to get a change for it.
- */
- bRestored = true;
- }
- hr = HrGetOneProp(lpFolder, PR_PARENT_SOURCE_KEY, &~lpPropVal);
- if(hr != hrSuccess)
- return hr;
-
- //check if we have to move the folder
- if(bRestored || lpPropVal->Value.bin.cb != lpPropParentSourceKey->Value.bin.cb || memcmp(lpPropVal->Value.bin.lpb, lpPropParentSourceKey->Value.bin.lpb, lpPropVal->Value.bin.cb) != 0){
- if(lpPropParentSourceKey->Value.bin.cb > 0){
- hr = m_lpFolder->GetMsgStore()->lpTransport->HrEntryIDFromSourceKey(m_lpFolder->GetMsgStore()->m_cbEntryId, m_lpFolder->GetMsgStore()->m_lpEntryId , lpPropParentSourceKey->Value.bin.cb, lpPropParentSourceKey->Value.bin.lpb, 0, NULL, &cbDestEntryId, &~lpDestEntryId);
- if (hr == MAPI_E_NOT_FOUND)
- //move to a folder we don't have
- return m_lpFolder->lpFolderOps->HrDeleteFolder(cbEntryId, lpEntryId, DEL_FOLDERS | DEL_MESSAGES | DELETE_HARD_DELETE, m_ulSyncId);
- if(hr != hrSuccess)
- return hr;
- }else{
- cbDestEntryId = m_lpFolder->m_cbEntryId;
- hr = MAPIAllocateBuffer(cbDestEntryId, &~lpDestEntryId);
- if(hr != hrSuccess)
- return hr;
- memcpy(lpDestEntryId, m_lpFolder->m_lpEntryId, cbDestEntryId);
- }
-
- // Do the move
- hr = m_lpFolder->lpFolderOps->HrCopyFolder(cbEntryId, lpEntryId, cbDestEntryId, lpDestEntryId, utf8string(), FOLDER_MOVE, m_ulSyncId);
- if(hr != hrSuccess)
- return hr;
- }
- }
-
- //ignore change if remote changekey is in local changelist
- if (lpPropChangeKey && HrGetOneProp(lpFolder, PR_PREDECESSOR_CHANGE_LIST, &~lpPropVal) == hrSuccess) {
- strChangeList.assign((char *)lpPropVal->Value.bin.lpb, lpPropVal->Value.bin.cb);
- ulPos = 0;
- while(ulPos < strChangeList.size()){
- ulSize = strChangeList.at(ulPos);
- if (ulSize <= sizeof(GUID))
- break;
- else if (ulSize == lpPropChangeKey->Value.bin.cb &&
- memcmp(strChangeList.substr(ulPos+1, ulSize).c_str(), lpPropChangeKey->Value.bin.lpb, ulSize) == 0)
- return SYNC_E_IGNORE;
- ulPos += ulSize + 1;
- }
- }
- //ignore change if local changekey in remote changelist
- if (lpPropChangeList && HrGetOneProp(lpFolder, PR_CHANGE_KEY, &~lpPropVal) == hrSuccess) {
- strChangeList.assign((char *)lpPropChangeList->Value.bin.lpb, lpPropChangeList->Value.bin.cb);
- ulPos = 0;
- while(ulPos < strChangeList.size()){
- ulSize = strChangeList.at(ulPos);
- if(ulSize <= sizeof(GUID)){
- break;
- }else if(lpPropVal->Value.bin.cb > sizeof(GUID) && memcmp(strChangeList.substr(ulPos+1, ulSize).c_str(), lpPropVal->Value.bin.lpb, sizeof(GUID)) == 0){
- bConflict = !(ulSize == lpPropVal->Value.bin.cb && memcmp(strChangeList.substr(ulPos+1, ulSize).c_str(), lpPropVal->Value.bin.lpb, ulSize) == 0);
- break;
- }
- ulPos += ulSize + 1;
- }
- }
- if(bConflict){
- //TODO: handle conflicts
- }
- hr = lpFolder->QueryInterface(IID_ECMAPIFolder, &~lpECFolder);
- if(hr != hrSuccess)
- return hr;
- hr = lpECFolder->HrSetSyncId(m_ulSyncId);
- if(hr != hrSuccess)
- return hr;
- hr = lpECFolder->SetProps(cValue, lpPropArray, NULL);
- if(hr != hrSuccess)
- return hr;
- hr = lpECFolder->SaveChanges(KEEP_OPEN_READWRITE);
- if(hr != hrSuccess)
- return hr;
- /**
- * If PR_ADDITIONAL_REN_ENTRYIDS exist this is assumed to be either the Inbox or the root-container. The
- * root container is only synced during the initial folder sync, but we'll perform the check here anyway.
- * If we have a PR_ADDITIONAL_REN_ENTRYIDS on the inbox, we'll set the same value on the root-container as
- * they're supposed to be in sync.
- * NOTE: This is a workaround for Kopano not handling this property (and some others) as special properties.
- */
- if (lpPropAdditionalREN != NULL && lpPropEntryId != NULL && lpPropEntryId->Value.bin.cb > 0) {
- HRESULT hrTmp = hrSuccess;
- MAPIFolderPtr ptrRoot;
- hrTmp = m_lpFolder->OpenEntry(0, nullptr, &ptrRoot.iid(), MAPI_BEST_ACCESS | MAPI_DEFERRED_ERRORS, &ulObjType, &~ptrRoot);
- if (hrTmp != hrSuccess)
- return hr;
- hrTmp = ptrRoot->SetProps(1, lpPropAdditionalREN, NULL);
- if (hrTmp != hrSuccess)
- return hr;
- hrTmp = ptrRoot->SaveChanges(KEEP_OPEN_READWRITE);
- if (hrTmp != hrSuccess)
- return hr;
- hrTmp = ECExchangeImportContentsChanges::HrUpdateSearchReminders(ptrRoot, lpPropAdditionalREN);
- }
- return hrSuccess;
- }
- //ulFlags = SYNC_SOFT_DELETE, SYNC_EXPIRY
- HRESULT ECExchangeImportHierarchyChanges::ImportFolderDeletion(ULONG ulFlags, LPENTRYLIST lpSourceEntryList){
- HRESULT hr = hrSuccess;
- ULONG ulSKNr;
- ULONG cbEntryId;
- for (ulSKNr = 0; ulSKNr < lpSourceEntryList->cValues; ++ulSKNr) {
- memory_ptr<ENTRYID> lpEntryId;
- hr = m_lpFolder->GetMsgStore()->lpTransport->HrEntryIDFromSourceKey(m_lpFolder->GetMsgStore()->m_cbEntryId, m_lpFolder->GetMsgStore()->m_lpEntryId, lpSourceEntryList->lpbin[ulSKNr].cb, lpSourceEntryList->lpbin[ulSKNr].lpb, 0, NULL, &cbEntryId, &~lpEntryId);
- if (hr == MAPI_E_NOT_FOUND) {
- hr = hrSuccess;
- continue;
- }
- if (hr != hrSuccess)
- break;
- hr = m_lpFolder->lpFolderOps->HrDeleteFolder(cbEntryId, lpEntryId, DEL_FOLDERS | DEL_MESSAGES, m_ulSyncId);
- if (hr != hrSuccess)
- break;
- }
- return hr;
- }
- DEF_ULONGMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, AddRef, (void))
- DEF_ULONGMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, Release, (void))
- DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, QueryInterface, (REFIID, refiid), (void **, lppInterface))
- DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, GetLastError, (HRESULT, hError), (ULONG, ulFlags), (LPMAPIERROR *, lppMapiError))
- DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, Config, (LPSTREAM, lpStream), (ULONG, ulFlags))
- DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, UpdateState, (LPSTREAM, lpStream))
- DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, ImportFolderChange, (ULONG, cValue), (LPSPropValue, lpPropArray))
- DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, ImportFolderDeletion, (ULONG, ulFlags), (LPENTRYLIST, lpSourceEntryList))
|