ECExchangeImportHierarchyChanges.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <new>
  18. #include <kopano/platform.h>
  19. #include <kopano/ECInterfaceDefs.h>
  20. #include <kopano/memory.hpp>
  21. #include "ECExchangeImportHierarchyChanges.h"
  22. #include "ECExchangeImportContentsChanges.h"
  23. #include <kopano/Util.h>
  24. #include <kopano/ECGuid.h>
  25. #include <edkguid.h>
  26. #include <mapiguid.h>
  27. #include <kopano/mapiext.h>
  28. #include <kopano/ECDebug.h>
  29. #include <kopano/stringutil.h>
  30. #include "pcutil.hpp"
  31. #include "ics.h"
  32. #include <mapiutil.h>
  33. #include "Mem.h"
  34. #include <kopano/mapi_ptr.h>
  35. #include "EntryPoint.h"
  36. #include <kopano/charset/convert.h>
  37. #include <kopano/charset/utf8string.h>
  38. #include <kopano/charset/convstring.h>
  39. using namespace KCHL;
  40. ECExchangeImportHierarchyChanges::ECExchangeImportHierarchyChanges(ECMAPIFolder *lpFolder) :
  41. m_lpFolder(lpFolder)
  42. {
  43. m_lpFolder->AddRef();
  44. }
  45. ECExchangeImportHierarchyChanges::~ECExchangeImportHierarchyChanges(){
  46. m_lpFolder->Release();
  47. }
  48. HRESULT ECExchangeImportHierarchyChanges::Create(ECMAPIFolder *lpFolder, LPEXCHANGEIMPORTHIERARCHYCHANGES* lppExchangeImportHierarchyChanges){
  49. if(!lpFolder)
  50. return MAPI_E_INVALID_PARAMETER;
  51. auto lpEIHC = new(std::nothrow) ECExchangeImportHierarchyChanges(lpFolder);
  52. if (lpEIHC == nullptr)
  53. return MAPI_E_NOT_ENOUGH_MEMORY;
  54. auto ret = lpEIHC->QueryInterface(IID_IExchangeImportHierarchyChanges,
  55. reinterpret_cast<void **>(lppExchangeImportHierarchyChanges));
  56. if (ret != hrSuccess)
  57. delete lpEIHC;
  58. return ret;
  59. }
  60. HRESULT ECExchangeImportHierarchyChanges::QueryInterface(REFIID refiid, void **lppInterface)
  61. {
  62. REGISTER_INTERFACE2(ECExchangeImportHierarchyChanges, this);
  63. REGISTER_INTERFACE2(ECUnknown, this);
  64. REGISTER_INTERFACE2(IExchangeImportHierarchyChanges, &this->m_xExchangeImportHierarchyChanges);
  65. REGISTER_INTERFACE2(IUnknown, &this->m_xExchangeImportHierarchyChanges);
  66. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  67. }
  68. HRESULT ECExchangeImportHierarchyChanges::GetLastError(HRESULT hResult, ULONG ulFlags, LPMAPIERROR *lppMAPIError){
  69. HRESULT hr = hrSuccess;
  70. ecmem_ptr<MAPIERROR> lpMapiError;
  71. memory_ptr<TCHAR> lpszErrorMsg;
  72. //FIXME: give synchronization errors messages
  73. hr = Util::HrMAPIErrorToText((hResult == hrSuccess)?MAPI_E_NO_ACCESS : hResult, &~lpszErrorMsg);
  74. if (hr != hrSuccess)
  75. return hr;
  76. hr = ECAllocateBuffer(sizeof(MAPIERROR), &~lpMapiError);
  77. if(hr != hrSuccess)
  78. return hr;
  79. if (ulFlags & MAPI_UNICODE) {
  80. std::wstring wstrErrorMsg = convert_to<std::wstring>(lpszErrorMsg.get());
  81. std::wstring wstrCompName = convert_to<std::wstring>(g_strProductName.c_str());
  82. if ((hr = MAPIAllocateMore(sizeof(std::wstring::value_type) * (wstrErrorMsg.size() + 1), lpMapiError, (void**)&lpMapiError->lpszError)) != hrSuccess)
  83. return hr;
  84. wcscpy((wchar_t*)lpMapiError->lpszError, wstrErrorMsg.c_str());
  85. if ((hr = MAPIAllocateMore(sizeof(std::wstring::value_type) * (wstrCompName.size() + 1), lpMapiError, (void**)&lpMapiError->lpszComponent)) != hrSuccess)
  86. return hr;
  87. wcscpy((wchar_t*)lpMapiError->lpszComponent, wstrCompName.c_str());
  88. } else {
  89. std::string strErrorMsg = convert_to<std::string>(lpszErrorMsg.get());
  90. std::string strCompName = convert_to<std::string>(g_strProductName.c_str());
  91. if ((hr = MAPIAllocateMore(strErrorMsg.size() + 1, lpMapiError, (void**)&lpMapiError->lpszError)) != hrSuccess)
  92. return hr;
  93. strcpy((char*)lpMapiError->lpszError, strErrorMsg.c_str());
  94. if ((hr = MAPIAllocateMore(strCompName.size() + 1, lpMapiError, (void**)&lpMapiError->lpszComponent)) != hrSuccess)
  95. return hr;
  96. strcpy((char*)lpMapiError->lpszComponent, strCompName.c_str());
  97. }
  98. lpMapiError->ulContext = 0;
  99. lpMapiError->ulLowLevelError= 0;
  100. lpMapiError->ulVersion = 0;
  101. *lppMAPIError = lpMapiError.release();
  102. return hrSuccess;
  103. }
  104. HRESULT ECExchangeImportHierarchyChanges::Config(LPSTREAM lpStream, ULONG ulFlags){
  105. HRESULT hr = hrSuccess;
  106. LARGE_INTEGER zero = {{0,0}};
  107. ULONG ulLen = 0;
  108. memory_ptr<SPropValue> lpPropSourceKey;
  109. m_lpStream = lpStream;
  110. if(lpStream == NULL) {
  111. m_ulSyncId = 0;
  112. m_ulChangeId = 0;
  113. } else {
  114. hr = lpStream->Seek(zero, STREAM_SEEK_SET, NULL);
  115. if(hr != hrSuccess)
  116. return hr;
  117. hr = lpStream->Read(&m_ulSyncId, 4, &ulLen);
  118. if(hr != hrSuccess)
  119. return hr;
  120. if(ulLen != 4)
  121. return MAPI_E_INVALID_PARAMETER;
  122. hr = lpStream->Read(&m_ulChangeId, 4, &ulLen);
  123. if(hr != hrSuccess)
  124. return hr;
  125. if(ulLen != 4) {
  126. return MAPI_E_INVALID_PARAMETER;
  127. }
  128. hr = HrGetOneProp(&m_lpFolder->m_xMAPIFolder, PR_SOURCE_KEY, &~lpPropSourceKey);
  129. if(hr != hrSuccess)
  130. return hr;
  131. // The user specified the special sync key '0000000000000000', get a sync key from the server.
  132. if(m_ulSyncId == 0) {
  133. 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);
  134. if(hr != hrSuccess)
  135. return hr;
  136. }
  137. // 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
  138. // 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,
  139. // preventing local looping of items.
  140. }
  141. m_ulFlags = ulFlags;
  142. return hrSuccess;
  143. }
  144. //write into the stream 4 bytes syncid and 4 bytes changeid
  145. HRESULT ECExchangeImportHierarchyChanges::UpdateState(LPSTREAM lpStream){
  146. HRESULT hr;
  147. LARGE_INTEGER zero = {{0,0}};
  148. ULONG ulLen = 0;
  149. if(lpStream == NULL) {
  150. if (m_lpStream == NULL)
  151. return hrSuccess;
  152. lpStream = m_lpStream;
  153. }
  154. if(m_ulSyncId == 0)
  155. return hrSuccess; // config() called with NULL stream, so we'll ignore the UpdateState()
  156. hr = lpStream->Seek(zero, STREAM_SEEK_SET, NULL);
  157. if(hr != hrSuccess)
  158. return hr;
  159. hr = lpStream->Write(&m_ulSyncId, 4, &ulLen);
  160. if(hr != hrSuccess)
  161. return hr;
  162. if (m_ulSyncId == 0)
  163. m_ulChangeId = 0;
  164. return lpStream->Write(&m_ulChangeId, 4, &ulLen);
  165. }
  166. HRESULT ECExchangeImportHierarchyChanges::ImportFolderChange(ULONG cValue, LPSPropValue lpPropArray){
  167. HRESULT hr = hrSuccess;
  168. ////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.
  169. auto lpPropParentSourceKey = PCpropFindProp(lpPropArray, cValue, PR_PARENT_SOURCE_KEY);
  170. auto lpPropSourceKey = PCpropFindProp(lpPropArray, cValue, PR_SOURCE_KEY);
  171. auto lpPropDisplayName = PCpropFindProp(lpPropArray, cValue, PR_DISPLAY_NAME);
  172. auto lpPropComment = PCpropFindProp(lpPropArray, cValue, PR_COMMENT);
  173. auto lpPropChangeKey = PCpropFindProp(lpPropArray, cValue, PR_CHANGE_KEY);
  174. auto lpPropFolderType = PCpropFindProp(lpPropArray, cValue, PR_FOLDER_TYPE);
  175. auto lpPropChangeList = PCpropFindProp(lpPropArray, cValue, PR_PREDECESSOR_CHANGE_LIST);
  176. auto lpPropEntryId = PCpropFindProp(lpPropArray, cValue, PR_ENTRYID);
  177. auto lpPropAdditionalREN = PCpropFindProp(lpPropArray, cValue, PR_ADDITIONAL_REN_ENTRYIDS);
  178. memory_ptr<SPropValue> lpPropVal;
  179. memory_ptr<ENTRYID> lpEntryId, lpDestEntryId;
  180. ULONG cbEntryId;
  181. ULONG cbDestEntryId;
  182. ULONG ulObjType;
  183. object_ptr<IMAPIFolder> lpFolder, lpParentFolder;
  184. object_ptr<ECMAPIFolder> lpECFolder, lpECParentFolder;
  185. ULONG ulFolderType = FOLDER_GENERIC;
  186. utf8string strFolderComment;
  187. ULONG cbOrigEntryId = 0;
  188. BYTE *lpOrigEntryId = NULL;
  189. const SBinary *lpOrigSourceKey = NULL;
  190. std::string strChangeList;
  191. ULONG ulPos = 0;
  192. ULONG ulSize = 0;
  193. bool bConflict = false;
  194. if (lpPropParentSourceKey == nullptr || lpPropSourceKey == nullptr ||
  195. lpPropDisplayName == nullptr)
  196. return MAPI_E_CALL_FAILED;
  197. if (lpPropComment)
  198. strFolderComment = convert_to<utf8string>(lpPropComment->Value.lpszW);
  199. if (lpPropEntryId && IsKopanoEntryId(lpPropEntryId->Value.bin.cb, lpPropEntryId->Value.bin.lpb)) {
  200. cbOrigEntryId = lpPropEntryId->Value.bin.cb;
  201. lpOrigEntryId = lpPropEntryId->Value.bin.lpb;
  202. }
  203. if (lpPropSourceKey != nullptr)
  204. lpOrigSourceKey = &lpPropSourceKey->Value.bin;
  205. if (lpPropFolderType != nullptr)
  206. ulFolderType = lpPropFolderType->Value.ul;
  207. if (ulFolderType == FOLDER_SEARCH)
  208. //ignore search folder
  209. return hrSuccess;
  210. 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);
  211. if(hr == MAPI_E_NOT_FOUND){
  212. // Folder is not yet available in our store
  213. if(lpPropParentSourceKey->Value.bin.cb > 0){
  214. // Find the parent folder in which the new folder is to be created
  215. 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);
  216. if(hr != hrSuccess)
  217. return hr;
  218. if (cbEntryId == 0)
  219. return MAPI_E_CALL_FAILED;
  220. hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpParentFolder);
  221. if(hr != hrSuccess)
  222. return hr;
  223. hr = lpParentFolder->QueryInterface(IID_ECMAPIFolder, &~lpECParentFolder);
  224. if(hr != hrSuccess)
  225. return hr;
  226. // Create the folder, loop through some names if it collides
  227. hr = lpECParentFolder->lpFolderOps->HrCreateFolder(ulFolderType, convstring(lpPropDisplayName->Value.lpszW), strFolderComment, 0, m_ulSyncId, lpOrigSourceKey, cbOrigEntryId, (LPENTRYID)lpOrigEntryId, &cbEntryId, &~lpEntryId);
  228. if(hr != hrSuccess)
  229. return hr;
  230. }else{
  231. hr = m_lpFolder->lpFolderOps->HrCreateFolder(ulFolderType, convstring(lpPropDisplayName->Value.lpszW), strFolderComment, 0, m_ulSyncId, lpOrigSourceKey, cbOrigEntryId, (LPENTRYID)lpOrigEntryId, &cbEntryId, &~lpEntryId);
  232. if (hr != hrSuccess)
  233. return hr;
  234. }
  235. // Open the folder we just created
  236. hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpFolder);
  237. if(hr != hrSuccess)
  238. return hr;
  239. }else if(hr != hrSuccess){
  240. return hr;
  241. }else if(cbEntryId == 0){
  242. return MAPI_E_CALL_FAILED;
  243. }else if(cbEntryId == m_lpFolder->m_cbEntryId && memcmp(lpEntryId, m_lpFolder->m_lpEntryId, cbEntryId)==0){
  244. // We are the changed folder
  245. hr = m_lpFolder->QueryInterface(IID_IMAPIFolder, &~lpFolder);
  246. if(hr != hrSuccess)
  247. return hr;
  248. }else{
  249. bool bRestored = false;
  250. // Changed folder is an existing subfolder
  251. hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpFolder);
  252. if(hr != hrSuccess){
  253. hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY | SHOW_SOFT_DELETES, &ulObjType, &~lpFolder);
  254. if(hr != hrSuccess)
  255. return hr;
  256. /**
  257. * If the folder was deleted locally, it must have been resotored remote in order to get a change for it.
  258. */
  259. bRestored = true;
  260. }
  261. hr = HrGetOneProp(lpFolder, PR_PARENT_SOURCE_KEY, &~lpPropVal);
  262. if(hr != hrSuccess)
  263. return hr;
  264. //check if we have to move the folder
  265. if(bRestored || lpPropVal->Value.bin.cb != lpPropParentSourceKey->Value.bin.cb || memcmp(lpPropVal->Value.bin.lpb, lpPropParentSourceKey->Value.bin.lpb, lpPropVal->Value.bin.cb) != 0){
  266. if(lpPropParentSourceKey->Value.bin.cb > 0){
  267. 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);
  268. if (hr == MAPI_E_NOT_FOUND)
  269. //move to a folder we don't have
  270. return m_lpFolder->lpFolderOps->HrDeleteFolder(cbEntryId, lpEntryId, DEL_FOLDERS | DEL_MESSAGES | DELETE_HARD_DELETE, m_ulSyncId);
  271. if(hr != hrSuccess)
  272. return hr;
  273. }else{
  274. cbDestEntryId = m_lpFolder->m_cbEntryId;
  275. hr = MAPIAllocateBuffer(cbDestEntryId, &~lpDestEntryId);
  276. if(hr != hrSuccess)
  277. return hr;
  278. memcpy(lpDestEntryId, m_lpFolder->m_lpEntryId, cbDestEntryId);
  279. }
  280. // Do the move
  281. hr = m_lpFolder->lpFolderOps->HrCopyFolder(cbEntryId, lpEntryId, cbDestEntryId, lpDestEntryId, utf8string(), FOLDER_MOVE, m_ulSyncId);
  282. if(hr != hrSuccess)
  283. return hr;
  284. }
  285. }
  286. //ignore change if remote changekey is in local changelist
  287. if (lpPropChangeKey && HrGetOneProp(lpFolder, PR_PREDECESSOR_CHANGE_LIST, &~lpPropVal) == hrSuccess) {
  288. strChangeList.assign((char *)lpPropVal->Value.bin.lpb, lpPropVal->Value.bin.cb);
  289. ulPos = 0;
  290. while(ulPos < strChangeList.size()){
  291. ulSize = strChangeList.at(ulPos);
  292. if (ulSize <= sizeof(GUID))
  293. break;
  294. else if (ulSize == lpPropChangeKey->Value.bin.cb &&
  295. memcmp(strChangeList.substr(ulPos+1, ulSize).c_str(), lpPropChangeKey->Value.bin.lpb, ulSize) == 0)
  296. return SYNC_E_IGNORE;
  297. ulPos += ulSize + 1;
  298. }
  299. }
  300. //ignore change if local changekey in remote changelist
  301. if (lpPropChangeList && HrGetOneProp(lpFolder, PR_CHANGE_KEY, &~lpPropVal) == hrSuccess) {
  302. strChangeList.assign((char *)lpPropChangeList->Value.bin.lpb, lpPropChangeList->Value.bin.cb);
  303. ulPos = 0;
  304. while(ulPos < strChangeList.size()){
  305. ulSize = strChangeList.at(ulPos);
  306. if(ulSize <= sizeof(GUID)){
  307. break;
  308. }else if(lpPropVal->Value.bin.cb > sizeof(GUID) && memcmp(strChangeList.substr(ulPos+1, ulSize).c_str(), lpPropVal->Value.bin.lpb, sizeof(GUID)) == 0){
  309. bConflict = !(ulSize == lpPropVal->Value.bin.cb && memcmp(strChangeList.substr(ulPos+1, ulSize).c_str(), lpPropVal->Value.bin.lpb, ulSize) == 0);
  310. break;
  311. }
  312. ulPos += ulSize + 1;
  313. }
  314. }
  315. if(bConflict){
  316. //TODO: handle conflicts
  317. }
  318. hr = lpFolder->QueryInterface(IID_ECMAPIFolder, &~lpECFolder);
  319. if(hr != hrSuccess)
  320. return hr;
  321. hr = lpECFolder->HrSetSyncId(m_ulSyncId);
  322. if(hr != hrSuccess)
  323. return hr;
  324. hr = lpECFolder->SetProps(cValue, lpPropArray, NULL);
  325. if(hr != hrSuccess)
  326. return hr;
  327. hr = lpECFolder->SaveChanges(KEEP_OPEN_READWRITE);
  328. if(hr != hrSuccess)
  329. return hr;
  330. /**
  331. * If PR_ADDITIONAL_REN_ENTRYIDS exist this is assumed to be either the Inbox or the root-container. The
  332. * root container is only synced during the initial folder sync, but we'll perform the check here anyway.
  333. * If we have a PR_ADDITIONAL_REN_ENTRYIDS on the inbox, we'll set the same value on the root-container as
  334. * they're supposed to be in sync.
  335. * NOTE: This is a workaround for Kopano not handling this property (and some others) as special properties.
  336. */
  337. if (lpPropAdditionalREN != NULL && lpPropEntryId != NULL && lpPropEntryId->Value.bin.cb > 0) {
  338. HRESULT hrTmp = hrSuccess;
  339. MAPIFolderPtr ptrRoot;
  340. hrTmp = m_lpFolder->OpenEntry(0, nullptr, &ptrRoot.iid(), MAPI_BEST_ACCESS | MAPI_DEFERRED_ERRORS, &ulObjType, &~ptrRoot);
  341. if (hrTmp != hrSuccess)
  342. return hr;
  343. hrTmp = ptrRoot->SetProps(1, lpPropAdditionalREN, NULL);
  344. if (hrTmp != hrSuccess)
  345. return hr;
  346. hrTmp = ptrRoot->SaveChanges(KEEP_OPEN_READWRITE);
  347. if (hrTmp != hrSuccess)
  348. return hr;
  349. hrTmp = ECExchangeImportContentsChanges::HrUpdateSearchReminders(ptrRoot, lpPropAdditionalREN);
  350. }
  351. return hrSuccess;
  352. }
  353. //ulFlags = SYNC_SOFT_DELETE, SYNC_EXPIRY
  354. HRESULT ECExchangeImportHierarchyChanges::ImportFolderDeletion(ULONG ulFlags, LPENTRYLIST lpSourceEntryList){
  355. HRESULT hr = hrSuccess;
  356. ULONG ulSKNr;
  357. ULONG cbEntryId;
  358. for (ulSKNr = 0; ulSKNr < lpSourceEntryList->cValues; ++ulSKNr) {
  359. memory_ptr<ENTRYID> lpEntryId;
  360. 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);
  361. if (hr == MAPI_E_NOT_FOUND) {
  362. hr = hrSuccess;
  363. continue;
  364. }
  365. if (hr != hrSuccess)
  366. break;
  367. hr = m_lpFolder->lpFolderOps->HrDeleteFolder(cbEntryId, lpEntryId, DEL_FOLDERS | DEL_MESSAGES, m_ulSyncId);
  368. if (hr != hrSuccess)
  369. break;
  370. }
  371. return hr;
  372. }
  373. DEF_ULONGMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, AddRef, (void))
  374. DEF_ULONGMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, Release, (void))
  375. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, QueryInterface, (REFIID, refiid), (void **, lppInterface))
  376. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, GetLastError, (HRESULT, hError), (ULONG, ulFlags), (LPMAPIERROR *, lppMapiError))
  377. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, Config, (LPSTREAM, lpStream), (ULONG, ulFlags))
  378. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, UpdateState, (LPSTREAM, lpStream))
  379. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, ImportFolderChange, (ULONG, cValue), (LPSPropValue, lpPropArray))
  380. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportHierarchyChanges, ExchangeImportHierarchyChanges, ImportFolderDeletion, (ULONG, ulFlags), (LPENTRYLIST, lpSourceEntryList))