ECExchangeImportContentsChanges.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  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/zcdefs.h>
  19. #include <kopano/platform.h>
  20. #include <kopano/ECInterfaceDefs.h>
  21. #include <kopano/memory.hpp>
  22. #include "ECExchangeImportContentsChanges.h"
  23. #include "WSMessageStreamImporter.h"
  24. #include "ECMessageStreamImporterIStreamAdapter.h"
  25. #include <kopano/ECLogger.h>
  26. #include "ECSyncLog.h"
  27. #include <kopano/Util.h>
  28. #include <edkguid.h>
  29. #include <kopano/ECGuid.h>
  30. #include <mapiguid.h>
  31. #include "ECMessage.h"
  32. #include <kopano/ECDebug.h>
  33. #include "ics.h"
  34. #include <kopano/mapiext.h>
  35. #include <kopano/mapi_ptr.h>
  36. #include <kopano/ECRestriction.h>
  37. #include <mapiutil.h>
  38. #include <kopano/charset/convert.h>
  39. #include <kopano/ECGetText.h>
  40. #include "EntryPoint.h"
  41. #include <list>
  42. using namespace KCHL;
  43. ECExchangeImportContentsChanges::ECExchangeImportContentsChanges(ECMAPIFolder *lpFolder)
  44. : m_lpFolder(lpFolder), m_iidMessage(IID_IMessage)
  45. {
  46. ECSyncLog::GetLogger(&m_lpLogger);
  47. m_lpFolder->AddRef();
  48. }
  49. ECExchangeImportContentsChanges::~ECExchangeImportContentsChanges(){
  50. m_lpFolder->Release();
  51. m_lpLogger->Release();
  52. MAPIFreeBuffer(m_lpSourceKey);
  53. }
  54. HRESULT ECExchangeImportContentsChanges::Create(ECMAPIFolder *lpFolder, LPEXCHANGEIMPORTCONTENTSCHANGES* lppExchangeImportContentsChanges){
  55. HRESULT hr;
  56. if(!lpFolder)
  57. return MAPI_E_INVALID_PARAMETER;
  58. auto lpEICC = new(std::nothrow) ECExchangeImportContentsChanges(lpFolder);
  59. if (lpEICC == nullptr)
  60. return MAPI_E_NOT_ENOUGH_MEMORY;
  61. hr = HrGetOneProp(&lpFolder->m_xMAPIProp, PR_SOURCE_KEY, &lpEICC->m_lpSourceKey);
  62. if (hr == hrSuccess)
  63. /*
  64. * This essentially returns lpEICC (in the second argument's
  65. * location), so whatever Coverity is saying about it not
  66. * being freed, don't believe it.
  67. */
  68. hr = lpEICC->QueryInterface(IID_IExchangeImportContentsChanges, reinterpret_cast<void **>(lppExchangeImportContentsChanges));
  69. else
  70. delete lpEICC;
  71. return hr;
  72. }
  73. HRESULT ECExchangeImportContentsChanges::QueryInterface(REFIID refiid, void **lppInterface)
  74. {
  75. BOOL bCanStream = FALSE;
  76. REGISTER_INTERFACE2(ECExchangeImportContentsChanges, this);
  77. REGISTER_INTERFACE2(ECUnknown, this);
  78. if (refiid == IID_IECImportContentsChanges) {
  79. m_lpFolder->GetMsgStore()->lpTransport->HrCheckCapabilityFlags(KOPANO_CAP_ENHANCED_ICS, &bCanStream);
  80. if (bCanStream == FALSE)
  81. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  82. REGISTER_INTERFACE2(IECImportContentsChanges, &this->m_xECImportContentsChanges);
  83. }
  84. REGISTER_INTERFACE2(IExchangeImportContentsChanges, &this->m_xECImportContentsChanges);
  85. REGISTER_INTERFACE2(IUnknown, &this->m_xECImportContentsChanges);
  86. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  87. }
  88. HRESULT ECExchangeImportContentsChanges::GetLastError(HRESULT hResult, ULONG ulFlags, LPMAPIERROR *lppMAPIError){
  89. HRESULT hr = hrSuccess;
  90. LPMAPIERROR lpMapiError = NULL;
  91. memory_ptr<TCHAR> lpszErrorMsg;
  92. //FIXME: give synchronization errors messages
  93. hr = Util::HrMAPIErrorToText((hResult == hrSuccess)?MAPI_E_NO_ACCESS : hResult, &~lpszErrorMsg);
  94. if (hr != hrSuccess)
  95. return hr;
  96. if ((hr = MAPIAllocateBuffer(sizeof(MAPIERROR),(void **)&lpMapiError)) != hrSuccess)
  97. return hr;
  98. if (ulFlags & MAPI_UNICODE) {
  99. std::wstring wstrErrorMsg = convert_to<std::wstring>(lpszErrorMsg.get());
  100. std::wstring wstrCompName = convert_to<std::wstring>(g_strProductName.c_str());
  101. if ((hr = MAPIAllocateMore(sizeof(std::wstring::value_type) * (wstrErrorMsg.size() + 1), lpMapiError, (void**)&lpMapiError->lpszError)) != hrSuccess)
  102. return hr;
  103. wcscpy((wchar_t*)lpMapiError->lpszError, wstrErrorMsg.c_str());
  104. if ((hr = MAPIAllocateMore(sizeof(std::wstring::value_type) * (wstrCompName.size() + 1), lpMapiError, (void**)&lpMapiError->lpszComponent)) != hrSuccess)
  105. return hr;
  106. wcscpy((wchar_t*)lpMapiError->lpszComponent, wstrCompName.c_str());
  107. } else {
  108. std::string strErrorMsg = convert_to<std::string>(lpszErrorMsg.get());
  109. std::string strCompName = convert_to<std::string>(g_strProductName.c_str());
  110. if ((hr = MAPIAllocateMore(strErrorMsg.size() + 1, lpMapiError, (void**)&lpMapiError->lpszError)) != hrSuccess)
  111. return hr;
  112. strcpy((char*)lpMapiError->lpszError, strErrorMsg.c_str());
  113. if ((hr = MAPIAllocateMore(strCompName.size() + 1, lpMapiError, (void**)&lpMapiError->lpszComponent)) != hrSuccess)
  114. return hr;
  115. strcpy((char*)lpMapiError->lpszComponent, strCompName.c_str());
  116. }
  117. lpMapiError->ulContext = 0;
  118. lpMapiError->ulLowLevelError= 0;
  119. lpMapiError->ulVersion = 0;
  120. *lppMAPIError = lpMapiError;
  121. return hrSuccess;
  122. }
  123. HRESULT ECExchangeImportContentsChanges::Config(LPSTREAM lpStream, ULONG ulFlags){
  124. HRESULT hr = hrSuccess;
  125. LARGE_INTEGER zero = {{0,0}};
  126. ULONG ulLen = 0;
  127. m_lpStream = lpStream;
  128. if(lpStream == NULL) {
  129. m_ulSyncId = 0;
  130. m_ulChangeId = 0;
  131. } else {
  132. hr = lpStream->Seek(zero, STREAM_SEEK_SET, NULL);
  133. if(hr != hrSuccess)
  134. return hr;
  135. hr = lpStream->Read(&m_ulSyncId, 4, &ulLen);
  136. if(hr != hrSuccess)
  137. return hr;
  138. if (ulLen != 4)
  139. return MAPI_E_INVALID_PARAMETER;
  140. hr = lpStream->Read(&m_ulChangeId, 4, &ulLen);
  141. if(hr != hrSuccess)
  142. return hr;
  143. if (ulLen != 4)
  144. return MAPI_E_INVALID_PARAMETER;
  145. // The user specified the special sync key '0000000000000000', get a sync key from the server.
  146. if(m_ulSyncId == 0) {
  147. hr = m_lpFolder->GetMsgStore()->lpTransport->HrSetSyncStatus(std::string((char *)m_lpSourceKey->Value.bin.lpb, m_lpSourceKey->Value.bin.cb), m_ulSyncId, m_ulChangeId, ICS_SYNC_CONTENTS, 0, &m_ulSyncId);
  148. if(hr != hrSuccess)
  149. return hr;
  150. }
  151. // 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
  152. // 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,
  153. // preventing local looping of items.
  154. }
  155. m_ulFlags = ulFlags;
  156. return hrSuccess;
  157. }
  158. HRESULT ECExchangeImportContentsChanges::UpdateState(LPSTREAM lpStream){
  159. HRESULT hr;
  160. LARGE_INTEGER zero = {{0,0}};
  161. ULONG ulLen = 0;
  162. if(lpStream == NULL) {
  163. if (m_lpStream == NULL)
  164. return hrSuccess;
  165. lpStream = m_lpStream;
  166. }
  167. if(m_ulSyncId == 0)
  168. return hrSuccess; // config() called with NULL stream, so we'll ignore the UpdateState()
  169. hr = lpStream->Seek(zero, STREAM_SEEK_SET, NULL);
  170. if(hr != hrSuccess)
  171. return hr;
  172. hr = lpStream->Write(&m_ulSyncId, 4, &ulLen);
  173. if(hr != hrSuccess)
  174. return hr;
  175. hr = lpStream->Write(&m_ulChangeId, 4, &ulLen);
  176. if(hr != hrSuccess)
  177. return hr;
  178. return hrSuccess;
  179. }
  180. HRESULT ECExchangeImportContentsChanges::ImportMessageChange(ULONG cValue, LPSPropValue lpPropArray, ULONG ulFlags, LPMESSAGE * lppMessage){
  181. HRESULT hr = hrSuccess;
  182. memory_ptr<SPropValue> lpPropPCL, lpPropCK;
  183. ULONG cbEntryId = 0;
  184. memory_ptr<ENTRYID> lpEntryId;
  185. auto lpMessageSourceKey = PCpropFindProp(lpPropArray, cValue, PR_SOURCE_KEY);
  186. auto lpMessageFlags = PCpropFindProp(lpPropArray, cValue, PR_MESSAGE_FLAGS);
  187. auto lpMessageAssociated = PCpropFindProp(lpPropArray, cValue, PR_ASSOCIATED);
  188. auto lpRemotePCL = PCpropFindProp(lpPropArray, cValue, PR_PREDECESSOR_CHANGE_LIST);
  189. auto lpRemoteCK = PCpropFindProp(lpPropArray, cValue, PR_CHANGE_KEY);
  190. ULONG ulObjType = 0;
  191. bool bAssociatedMessage = false;
  192. IMessage *lpMessage = NULL;
  193. object_ptr<ECMessage> lpECMessage;
  194. ULONG ulNewFlags = 0;
  195. if(lpMessageSourceKey != NULL) {
  196. hr = m_lpFolder->GetMsgStore()->lpTransport->HrEntryIDFromSourceKey(m_lpFolder->GetMsgStore()->m_cbEntryId, m_lpFolder->GetMsgStore()->m_lpEntryId, m_lpSourceKey->Value.bin.cb, m_lpSourceKey->Value.bin.lpb, lpMessageSourceKey->Value.bin.cb, lpMessageSourceKey->Value.bin.lpb, &cbEntryId, &~lpEntryId);
  197. if(hr != MAPI_E_NOT_FOUND && hr != hrSuccess)
  198. return hr;
  199. } else {
  200. // Source key not specified, therefore the message must be new since this is the only thing
  201. // we can do if there is no sourcekey. Z-Push uses this, while offline ICS does not (it always
  202. // passes a source key)
  203. ulFlags |= SYNC_NEW_MESSAGE;
  204. hr = MAPI_E_NOT_FOUND;
  205. }
  206. if (hr == MAPI_E_NOT_FOUND && (ulFlags & SYNC_NEW_MESSAGE) == 0)
  207. // This is a change, but we don't already have the item. This can only mean
  208. // that the item has been deleted on our side.
  209. return SYNC_E_OBJECT_DELETED;
  210. if ((lpMessageFlags != NULL &&
  211. (lpMessageFlags->Value.ul & MSGFLAG_ASSOCIATED)) ||
  212. (lpMessageAssociated != NULL && lpMessageAssociated->Value.b))
  213. bAssociatedMessage = true;
  214. if(hr == MAPI_E_NOT_FOUND){
  215. if (bAssociatedMessage)
  216. ulNewFlags = MAPI_ASSOCIATED;
  217. else
  218. ulNewFlags = 0;
  219. auto lpPassedEntryId = PCpropFindProp(lpPropArray, cValue, PR_ENTRYID);
  220. // Create the message with the passed entry ID
  221. if(lpPassedEntryId)
  222. hr = m_lpFolder->CreateMessageWithEntryID(&IID_IMessage, ulNewFlags, lpPassedEntryId->Value.bin.cb, (LPENTRYID)lpPassedEntryId->Value.bin.lpb, &lpMessage);
  223. else
  224. hr = m_lpFolder->CreateMessage(&IID_IMessage, ulNewFlags, &lpMessage);
  225. if(hr != hrSuccess)
  226. return hr;
  227. }else{
  228. hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &m_iidMessage, MAPI_MODIFY, &ulObjType, (LPUNKNOWN*)&lpMessage);
  229. if (hr == MAPI_E_NOT_FOUND)
  230. // The item was soft-deleted; sourcekey is known, but we cannot open the item. It has therefore been deleted.
  231. return SYNC_E_OBJECT_DELETED;
  232. if(hr != hrSuccess)
  233. return hr;
  234. if (IsProcessed(lpRemoteCK, lpPropPCL))
  235. //we already have this change
  236. return SYNC_E_IGNORE;
  237. // Check for conflicts except for associated messages, take always the lastone
  238. if (bAssociatedMessage == false &&
  239. HrGetOneProp(lpMessage, PR_CHANGE_KEY, &~lpPropCK) == hrSuccess &&
  240. IsConflict(lpPropCK, lpRemotePCL) &&
  241. CreateConflictMessage(lpMessage) == MAPI_E_NOT_FOUND){
  242. CreateConflictFolders();
  243. CreateConflictMessage(lpMessage);
  244. }
  245. }
  246. hr = lpMessage->QueryInterface(IID_ECMessage, &~lpECMessage);
  247. if(hr != hrSuccess)
  248. return hr;
  249. hr = lpECMessage->HrSetSyncId(m_ulSyncId);
  250. if(hr != hrSuccess)
  251. return hr;
  252. // Mark as ICS object
  253. hr = lpECMessage->SetICSObject(TRUE);
  254. if(hr != hrSuccess)
  255. return hr;
  256. hr = lpMessage->SetProps(cValue, lpPropArray, NULL);
  257. if(hr != hrSuccess)
  258. return hr;
  259. *lppMessage = lpMessage;
  260. return hrSuccess;
  261. }
  262. //ulFlags = SYNC_SOFT_DELETE, SYNC_EXPIRY
  263. HRESULT ECExchangeImportContentsChanges::ImportMessageDeletion(ULONG ulFlags, LPENTRYLIST lpSourceEntryList){
  264. HRESULT hr = hrSuccess;
  265. ENTRYLIST EntryList;
  266. ULONG ulSKNr;
  267. EntryList.lpbin = NULL;
  268. EntryList.cValues = 0;
  269. if ((hr = MAPIAllocateBuffer(sizeof(SBinary)* lpSourceEntryList->cValues, (LPVOID*)&EntryList.lpbin)) != hrSuccess)
  270. goto exit;
  271. for (ulSKNr = 0; ulSKNr < lpSourceEntryList->cValues; ++ulSKNr) {
  272. hr = m_lpFolder->GetMsgStore()->lpTransport->HrEntryIDFromSourceKey(m_lpFolder->GetMsgStore()->m_cbEntryId, m_lpFolder->GetMsgStore()->m_lpEntryId, m_lpSourceKey->Value.bin.cb, m_lpSourceKey->Value.bin.lpb, lpSourceEntryList->lpbin[ulSKNr].cb, lpSourceEntryList->lpbin[ulSKNr].lpb, &EntryList.lpbin[EntryList.cValues].cb, (LPENTRYID*)&EntryList.lpbin[EntryList.cValues].lpb);
  273. if(hr == MAPI_E_NOT_FOUND){
  274. hr = hrSuccess;
  275. continue;
  276. }
  277. if(hr != hrSuccess)
  278. goto exit;
  279. ++EntryList.cValues;
  280. }
  281. if(EntryList.cValues == 0)
  282. goto exit;
  283. hr = m_lpFolder->GetMsgStore()->lpTransport->HrDeleteObjects(ulFlags & SYNC_SOFT_DELETE ? 0 : DELETE_HARD_DELETE, &EntryList, m_ulSyncId);
  284. if(hr != hrSuccess)
  285. goto exit;
  286. exit:
  287. if(EntryList.lpbin){
  288. for (ulSKNr = 0; ulSKNr < EntryList.cValues; ++ulSKNr)
  289. MAPIFreeBuffer(EntryList.lpbin[ulSKNr].lpb);
  290. MAPIFreeBuffer(EntryList.lpbin);
  291. }
  292. return hr;
  293. }
  294. HRESULT ECExchangeImportContentsChanges::ImportPerUserReadStateChange(ULONG cElements, LPREADSTATE lpReadState){
  295. HRESULT hr = hrSuccess;
  296. ULONG ulSKNr, cbEntryId;
  297. for (ulSKNr = 0; ulSKNr < cElements; ++ulSKNr) {
  298. memory_ptr<ENTRYID> lpEntryId;
  299. hr = m_lpFolder->GetMsgStore()->lpTransport->HrEntryIDFromSourceKey(m_lpFolder->GetMsgStore()->m_cbEntryId, m_lpFolder->GetMsgStore()->m_lpEntryId , m_lpSourceKey->Value.bin.cb, m_lpSourceKey->Value.bin.lpb, lpReadState[ulSKNr].cbSourceKey, lpReadState[ulSKNr].pbSourceKey, &cbEntryId, &~lpEntryId);
  300. if(hr == MAPI_E_NOT_FOUND){
  301. hr = hrSuccess;
  302. continue; // Message is delete or moved
  303. }
  304. if (hr != hrSuccess)
  305. return hr;
  306. hr = m_lpFolder->GetMsgStore()->lpTransport->HrSetReadFlag(cbEntryId, lpEntryId, lpReadState[ulSKNr].ulFlags & MSGFLAG_READ ? 0 : CLEAR_READ_FLAG, m_ulSyncId);
  307. if(hr != hrSuccess)
  308. return hr;
  309. }
  310. return hrSuccess;
  311. }
  312. HRESULT ECExchangeImportContentsChanges::ImportMessageMove(ULONG cbSourceKeySrcFolder, BYTE *pbSourceKeySrcFolder, ULONG cbSourceKeySrcMessage, BYTE *pbSourceKeySrcMessage, ULONG cbPCLMessage, BYTE *pbPCLMessage, ULONG cbSourceKeyDestMessage, BYTE *pbSourceKeyDestMessage, ULONG cbChangeNumDestMessage, BYTE *pbChangeNumDestMessage){
  313. return MAPI_E_NO_SUPPORT;
  314. }
  315. /** Check if the imported change has already been processed
  316. *
  317. * This is done by checking if the remote change key (or a newer change from the same source) is present
  318. * in the local predecessor change list.
  319. *
  320. * @param[in] lpRemoteCK The remote change key
  321. * @param[in] lpLocalPCL The local predecessor change list
  322. *
  323. * @return boolean
  324. * @return true The change has been processed before.
  325. * @return false The change hasn't been processed yet.
  326. */
  327. bool ECExchangeImportContentsChanges::IsProcessed(const SPropValue *lpRemoteCK,
  328. const SPropValue *lpLocalPCL)
  329. {
  330. if (!lpRemoteCK || !lpLocalPCL)
  331. return false;
  332. assert(lpRemoteCK->ulPropTag == PR_CHANGE_KEY);
  333. assert(lpLocalPCL->ulPropTag == PR_PREDECESSOR_CHANGE_LIST);
  334. const std::string strChangeList((char*)lpLocalPCL->Value.bin.lpb, lpLocalPCL->Value.bin.cb);
  335. size_t ulPos = 0;
  336. while (ulPos < strChangeList.size()) {
  337. size_t ulSize = strChangeList.at(ulPos++);
  338. if (ulSize <= sizeof(GUID))
  339. break;
  340. else if (lpRemoteCK->Value.bin.cb > sizeof(GUID) &&
  341. memcmp(strChangeList.data() + ulPos, lpRemoteCK->Value.bin.lpb, sizeof(GUID)) == 0 &&
  342. ulSize == lpRemoteCK->Value.bin.cb &&
  343. memcmp(strChangeList.data() + ulPos, lpRemoteCK->Value.bin.lpb, ulSize) == 0)
  344. //remote changekey in our changelist
  345. //we already have this change
  346. return true;
  347. ulPos += ulSize;
  348. }
  349. return false;
  350. }
  351. /** Check if the imported change conflicts with a local change.
  352. *
  353. * How this works
  354. *
  355. * We get
  356. * 1) the remote (exporter's) predecessor change list
  357. * 2) the item that will be modified (importer's) change key
  358. *
  359. * We then look at the remote change list, and find a change with the same GUID as the local change
  360. *
  361. * - If the trailing part (which increases with each change) is higher locally than is on the remote change list,
  362. * then there's a conflict since we have a newer version than the remote server has, and the remote server is sending
  363. * us a change.
  364. * - If the remote PCL does not contain an entry with the same GUID as our local change key, than there's a conflict since
  365. * we get a change from the other side while we have a change which is not seen yet by the other side.
  366. *
  367. * @param[in] lpLocalCK The local change key
  368. * @param[in] lpRemotePCL The remote predecessor change list
  369. *
  370. * @return boolean
  371. * @retval true The change conflicts with a local change
  372. * @retval false The change doesn't conflict with a local change.
  373. */
  374. bool ECExchangeImportContentsChanges::IsConflict(const SPropValue *lpLocalCK,
  375. const SPropValue *lpRemotePCL)
  376. {
  377. if (!lpLocalCK || !lpRemotePCL)
  378. return false;
  379. assert(lpLocalCK->ulPropTag == PR_CHANGE_KEY);
  380. assert(lpRemotePCL->ulPropTag == PR_PREDECESSOR_CHANGE_LIST);
  381. bool bConflict = false;
  382. bool bGuidFound = false;
  383. const std::string strChangeList((char*)lpRemotePCL->Value.bin.lpb, lpRemotePCL->Value.bin.cb);
  384. size_t ulPos = 0;
  385. while (!bConflict && ulPos < strChangeList.size()) {
  386. size_t ulSize = strChangeList.at(ulPos++);
  387. if (ulSize <= sizeof(GUID)) {
  388. break;
  389. } else if (lpLocalCK->Value.bin.cb > sizeof(GUID) && memcmp(strChangeList.data() + ulPos, lpLocalCK->Value.bin.lpb, sizeof(GUID)) == 0) {
  390. bGuidFound = true; // Track if we found the GUID from our local change key
  391. unsigned int ulRemoteChangeNumber = 0;
  392. unsigned int ulLocalChangeNumber = 0;
  393. ulRemoteChangeNumber = *(unsigned int *)(strChangeList.data() + ulPos + sizeof(GUID));
  394. ulLocalChangeNumber = *(unsigned int *)(lpLocalCK->Value.bin.lpb + sizeof(GUID));
  395. // We have a conflict if we have a newer change locally than the remove server is sending us.
  396. bConflict = ulLocalChangeNumber > ulRemoteChangeNumber;
  397. }
  398. ulPos += ulSize;
  399. }
  400. if (!bGuidFound)
  401. bConflict = true;
  402. return bConflict;
  403. }
  404. HRESULT ECExchangeImportContentsChanges::CreateConflictMessage(LPMESSAGE lpMessage)
  405. {
  406. HRESULT hr = hrSuccess;
  407. memory_ptr<SPropValue> lpConflictItems;
  408. hr = CreateConflictMessageOnly(lpMessage, &~lpConflictItems);
  409. if (hr != hrSuccess)
  410. return hr;
  411. hr = HrSetOneProp(lpMessage, lpConflictItems);
  412. if(hr != hrSuccess)
  413. return hr;
  414. return lpMessage->SaveChanges(KEEP_OPEN_READWRITE);
  415. }
  416. HRESULT ECExchangeImportContentsChanges::CreateConflictMessageOnly(LPMESSAGE lpMessage, LPSPropValue *lppConflictItems)
  417. {
  418. HRESULT hr = hrSuccess;
  419. object_ptr<IMAPIFolder> lpRootFolder, lpConflictFolder;
  420. object_ptr<IMessage> lpConflictMessage;
  421. memory_ptr<SPropValue> lpPropAdditionalREN;
  422. memory_ptr<SPropValue> lpConflictItems, lpEntryIdProp;
  423. LPSBinary lpEntryIds = NULL;
  424. ULONG ulCount = 0;
  425. ULONG ulObjType = 0;
  426. static constexpr const SizedSPropTagArray(5, excludeProps) =
  427. {5, {PR_ENTRYID, PR_CONFLICT_ITEMS, PR_SOURCE_KEY,
  428. PR_CHANGE_KEY, PR_PREDECESSOR_CHANGE_LIST}};
  429. //open the conflicts folder
  430. hr = m_lpFolder->GetMsgStore()->OpenEntry(0, nullptr, &IID_IMAPIFolder, 0, &ulObjType, &~lpRootFolder);
  431. if(hr != hrSuccess)
  432. return hr;
  433. hr = HrGetOneProp(lpRootFolder, PR_ADDITIONAL_REN_ENTRYIDS, &~lpPropAdditionalREN);
  434. if(hr != hrSuccess)
  435. return hr;
  436. if (lpPropAdditionalREN->Value.MVbin.cValues == 0 ||
  437. lpPropAdditionalREN->Value.MVbin.lpbin[0].cb == 0)
  438. return MAPI_E_NOT_FOUND;
  439. hr = m_lpFolder->GetMsgStore()->OpenEntry(lpPropAdditionalREN->Value.MVbin.lpbin[0].cb, reinterpret_cast<ENTRYID *>(lpPropAdditionalREN->Value.MVbin.lpbin[0].lpb), &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpConflictFolder);
  440. if(hr != hrSuccess)
  441. return hr;
  442. //create the conflict message
  443. hr = lpConflictFolder->CreateMessage(nullptr, 0, &~lpConflictMessage);
  444. if(hr != hrSuccess)
  445. return hr;
  446. hr = lpMessage->CopyTo(0, NULL, excludeProps, 0, NULL, &IID_IMessage,
  447. lpConflictMessage, 0, NULL);
  448. if(hr != hrSuccess)
  449. return hr;
  450. //set the entryid from original message in PR_CONFLICT_ITEMS of conflict message
  451. hr = HrGetOneProp(lpMessage, PR_ENTRYID, &~lpEntryIdProp);
  452. if(hr != hrSuccess)
  453. return hr;
  454. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpConflictItems);
  455. if(hr != hrSuccess)
  456. return hr;
  457. lpConflictItems->ulPropTag = PR_CONFLICT_ITEMS;
  458. lpConflictItems->Value.MVbin.cValues = 1;
  459. lpConflictItems->Value.MVbin.lpbin = &lpEntryIdProp->Value.bin;
  460. hr = HrSetOneProp(lpConflictMessage, lpConflictItems);
  461. if(hr != hrSuccess)
  462. return hr;
  463. hr = lpConflictMessage->SaveChanges(KEEP_OPEN_READWRITE);
  464. if(hr != hrSuccess)
  465. return hr;
  466. //add the entryid from the conflict message to the PR_CONFLICT_ITEMS of the original message
  467. hr = HrGetOneProp(lpConflictMessage, PR_ENTRYID, &~lpEntryIdProp);
  468. if(hr != hrSuccess)
  469. return hr;
  470. if (HrGetOneProp(lpMessage, PR_CONFLICT_ITEMS, &~lpConflictItems) != hrSuccess) {
  471. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpConflictItems);
  472. if(hr != hrSuccess)
  473. return hr;
  474. lpConflictItems->ulPropTag = PR_CONFLICT_ITEMS;
  475. lpConflictItems->Value.MVbin.cValues = 0;
  476. lpConflictItems->Value.MVbin.lpbin = NULL;
  477. }
  478. hr = MAPIAllocateMore(sizeof(SBinary)*(lpConflictItems->Value.MVbin.cValues+1), lpConflictItems, (LPVOID*)&lpEntryIds);
  479. if(hr != hrSuccess)
  480. return hr;
  481. for (ulCount = 0; ulCount < lpConflictItems->Value.MVbin.cValues; ++ulCount) {
  482. lpEntryIds[ulCount].cb = lpConflictItems->Value.MVbin.lpbin[ulCount].cb;
  483. lpEntryIds[ulCount].lpb = lpConflictItems->Value.MVbin.lpbin[ulCount].lpb;
  484. }
  485. lpEntryIds[ulCount].cb = lpEntryIdProp->Value.bin.cb;
  486. lpEntryIds[ulCount].lpb = lpEntryIdProp->Value.bin.lpb;
  487. lpConflictItems->Value.MVbin.lpbin = lpEntryIds;
  488. ++lpConflictItems->Value.MVbin.cValues;
  489. if (lppConflictItems)
  490. *lppConflictItems = lpConflictItems.release();
  491. return hrSuccess;
  492. }
  493. HRESULT ECExchangeImportContentsChanges::CreateConflictFolders(){
  494. HRESULT hr = hrSuccess;
  495. object_ptr<IMAPIFolder> lpRootFolder, lpParentFolder, lpInbox, lpConflictFolder;
  496. memory_ptr<SPropValue> lpAdditionalREN, lpNewAdditionalREN;
  497. memory_ptr<SPropValue> lpIPMSubTree;
  498. memory_ptr<ENTRYID> lpEntryId;
  499. ULONG cbEntryId = 0;
  500. ULONG ulObjType = 0;
  501. ULONG ulCount = 0;
  502. hr = m_lpFolder->OpenEntry(0, nullptr, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpRootFolder);
  503. if(hr != hrSuccess) {
  504. ZLOG_DEBUG(m_lpLogger, "Failed to open root folder, hr = 0x%08x", hr);
  505. return hr;
  506. }
  507. hr = m_lpFolder->GetMsgStore()->GetReceiveFolder((TCHAR*)"IPM", 0, &cbEntryId, &~lpEntryId, NULL);
  508. if(hr != hrSuccess) {
  509. ZLOG_DEBUG(m_lpLogger, "Failed to get 'IPM' receive folder id, hr = 0x%08x", hr);
  510. return hr;
  511. }
  512. hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpInbox);
  513. if(hr != hrSuccess) {
  514. ZLOG_DEBUG(m_lpLogger, "Failed to open 'IPM' receive folder, hr = 0x%08x", hr);
  515. return hr;
  516. }
  517. hr = HrGetOneProp(&m_lpFolder->GetMsgStore()->m_xMsgStore, PR_IPM_SUBTREE_ENTRYID, &~lpIPMSubTree);
  518. if(hr != hrSuccess) {
  519. ZLOG_DEBUG(m_lpLogger, "Failed to get ipm subtree id, hr = 0x%08x", hr);
  520. return hr;
  521. }
  522. hr = m_lpFolder->OpenEntry(lpIPMSubTree->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpIPMSubTree->Value.bin.lpb), &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpParentFolder);
  523. if(hr != hrSuccess) {
  524. ZLOG_DEBUG(m_lpLogger, "Failed to open ipm subtree folder, hr = 0x%08x", hr);
  525. return hr;
  526. }
  527. HrGetOneProp(lpRootFolder, PR_ADDITIONAL_REN_ENTRYIDS, &~lpAdditionalREN);
  528. //make new PR_ADDITIONAL_REN_ENTRYIDS
  529. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~lpNewAdditionalREN);
  530. if(hr != hrSuccess)
  531. return hr;
  532. lpNewAdditionalREN->ulPropTag = PR_ADDITIONAL_REN_ENTRYIDS;
  533. lpNewAdditionalREN->Value.MVbin.cValues = (lpAdditionalREN == nullptr || lpAdditionalREN->Value.MVbin.cValues < 4) ? 4 : lpAdditionalREN->Value.MVbin.cValues;
  534. hr = MAPIAllocateMore(sizeof(SBinary)*lpNewAdditionalREN->Value.MVbin.cValues, lpNewAdditionalREN, (LPVOID*)&lpNewAdditionalREN->Value.MVbin.lpbin);
  535. if(hr != hrSuccess)
  536. return hr;
  537. //copy from original PR_ADDITIONAL_REN_ENTRYIDS
  538. if(lpAdditionalREN)
  539. for (ulCount = 0; ulCount < lpAdditionalREN->Value.MVbin.cValues; ++ulCount)
  540. lpNewAdditionalREN->Value.MVbin.lpbin[ulCount] = lpAdditionalREN->Value.MVbin.lpbin[ulCount];
  541. hr = CreateConflictFolder(_("Sync Issues"), lpNewAdditionalREN, 1, lpParentFolder, &~lpConflictFolder);
  542. if(hr != hrSuccess) {
  543. ZLOG_DEBUG(m_lpLogger, "Failed to create 'Sync Issues' folder, hr = 0x%08x", hr);
  544. return hr;
  545. }
  546. hr = CreateConflictFolder(_("Conflicts"), lpNewAdditionalREN, 0, lpConflictFolder, NULL);
  547. if(hr != hrSuccess) {
  548. ZLOG_DEBUG(m_lpLogger, "Failed to create 'Conflicts' folder, hr = 0x%08x", hr);
  549. return hr;
  550. }
  551. hr = CreateConflictFolder(_("Local Failures"), lpNewAdditionalREN, 2, lpConflictFolder, NULL);
  552. if(hr != hrSuccess) {
  553. ZLOG_DEBUG(m_lpLogger, "Failed to create 'Local Failures' folder, hr = 0x%08x", hr);
  554. return hr;
  555. }
  556. hr = CreateConflictFolder(_("Server Failures"), lpNewAdditionalREN, 3, lpConflictFolder, NULL);
  557. if(hr != hrSuccess) {
  558. ZLOG_DEBUG(m_lpLogger, "Failed to create 'Server Failures' folder, hr = 0x%08x", hr);
  559. return hr;
  560. }
  561. hr = HrSetOneProp(lpRootFolder, lpNewAdditionalREN);
  562. if(hr != hrSuccess)
  563. return hr;
  564. hr = HrSetOneProp(lpInbox, lpNewAdditionalREN);
  565. if(hr != hrSuccess)
  566. return hr;
  567. hr = HrUpdateSearchReminders(lpRootFolder, lpNewAdditionalREN);
  568. if (hr == MAPI_E_NOT_FOUND) {
  569. m_lpLogger->Log(EC_LOGLEVEL_INFO, "No reminder searchfolder found, nothing to update");
  570. } else if (hr != hrSuccess) {
  571. ZLOG_DEBUG(m_lpLogger, "Failed to update search reminders, hr = 0x%08x", hr);
  572. return hr;
  573. }
  574. return hrSuccess;
  575. }
  576. HRESULT ECExchangeImportContentsChanges::CreateConflictFolder(LPTSTR lpszName, LPSPropValue lpAdditionalREN, ULONG ulMVPos, LPMAPIFOLDER lpParentFolder, LPMAPIFOLDER * lppConflictFolder){
  577. HRESULT hr = hrSuccess;
  578. object_ptr<IMAPIFolder> lpConflictFolder;
  579. memory_ptr<SPropValue> lpEntryId;
  580. SPropValue sPropValue;
  581. ULONG ulObjType = 0;
  582. if (lpAdditionalREN->Value.MVbin.lpbin[ulMVPos].cb > 0 &&
  583. lpParentFolder->OpenEntry(lpAdditionalREN->Value.MVbin.lpbin[ulMVPos].cb, reinterpret_cast<ENTRYID *>(lpAdditionalREN->Value.MVbin.lpbin[ulMVPos].lpb), &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpConflictFolder) == hrSuccess) {
  584. if(lppConflictFolder)
  585. *lppConflictFolder = lpConflictFolder.release();
  586. return hr;
  587. }
  588. hr = lpParentFolder->CreateFolder(FOLDER_GENERIC, lpszName, nullptr, &IID_IMAPIFolder, OPEN_IF_EXISTS | fMapiUnicode, &~lpConflictFolder);
  589. if(hr != hrSuccess)
  590. return hr;
  591. sPropValue.ulPropTag = PR_FOLDER_DISPLAY_FLAGS;
  592. sPropValue.Value.bin.cb = 6;
  593. sPropValue.Value.bin.lpb = (LPBYTE)"\x01\x04\x0A\x80\x1E\x00";
  594. hr = HrSetOneProp(lpConflictFolder, &sPropValue);
  595. if(hr != hrSuccess)
  596. return hr;
  597. hr = HrGetOneProp(lpConflictFolder, PR_ENTRYID, &~lpEntryId);
  598. if(hr != hrSuccess)
  599. return hr;
  600. hr = MAPIAllocateMore(lpEntryId->Value.bin.cb, lpAdditionalREN, (LPVOID*)&lpAdditionalREN->Value.MVbin.lpbin[ulMVPos].lpb);
  601. if(hr != hrSuccess)
  602. return hr;
  603. memcpy(lpAdditionalREN->Value.MVbin.lpbin[ulMVPos].lpb, lpEntryId->Value.bin.lpb, lpEntryId->Value.bin.cb);
  604. lpAdditionalREN->Value.MVbin.lpbin[ulMVPos].cb = lpEntryId->Value.bin.cb;
  605. if(lppConflictFolder)
  606. *lppConflictFolder = lpConflictFolder.release();
  607. return hrSuccess;
  608. }
  609. HRESULT ECExchangeImportContentsChanges::ConfigForConversionStream(LPSTREAM lpStream, ULONG ulFlags, ULONG /*cValuesConversion*/, LPSPropValue /*lpPropArrayConversion*/)
  610. {
  611. HRESULT hr;
  612. BOOL bCanStream = FALSE;
  613. // Since we don't use the cValuesConversion and lpPropArrayConversion arguments, we'll just check
  614. // if the server suppors streaming and if so call the 'normal' config.
  615. hr = m_lpFolder->GetMsgStore()->lpTransport->HrCheckCapabilityFlags(KOPANO_CAP_ENHANCED_ICS, &bCanStream);
  616. if (hr != hrSuccess)
  617. return hr;
  618. if (bCanStream == FALSE)
  619. return MAPI_E_NO_SUPPORT;
  620. return Config(lpStream, ulFlags);
  621. }
  622. HRESULT ECExchangeImportContentsChanges::ImportMessageChangeAsAStream(ULONG cValue, LPSPropValue lpPropArray, ULONG ulFlags, LPSTREAM *lppStream)
  623. {
  624. HRESULT hr;
  625. ULONG cbEntryId = 0;
  626. EntryIdPtr ptrEntryId;
  627. WSMessageStreamImporterPtr ptrMessageImporter;
  628. StreamPtr ptrStream;
  629. auto lpMessageSourceKey = PCpropFindProp(lpPropArray, cValue, PR_SOURCE_KEY);
  630. if (lpMessageSourceKey != NULL) {
  631. hr = m_lpFolder->GetMsgStore()->lpTransport->HrEntryIDFromSourceKey(m_lpFolder->GetMsgStore()->m_cbEntryId, m_lpFolder->GetMsgStore()->m_lpEntryId, m_lpSourceKey->Value.bin.cb, m_lpSourceKey->Value.bin.lpb, lpMessageSourceKey->Value.bin.cb, lpMessageSourceKey->Value.bin.lpb, &cbEntryId, &~ptrEntryId);
  632. if (hr != MAPI_E_NOT_FOUND && hr != hrSuccess) {
  633. ZLOG_DEBUG(m_lpLogger, "ImportFast: Failed to get entryid from sourcekey, hr = 0x%08x", hr);
  634. return hr;
  635. }
  636. } else {
  637. // Source key not specified, therefore the message must be new since this is the only thing
  638. // we can do if there is no sourcekey. Z-Push uses this, while offline ICS does not (it always
  639. // passes a source key)
  640. ulFlags |= SYNC_NEW_MESSAGE;
  641. hr = MAPI_E_NOT_FOUND;
  642. }
  643. if (hr == MAPI_E_NOT_FOUND && ((ulFlags & SYNC_NEW_MESSAGE) == 0)) {
  644. // This is a change, but we don't already have the item. This can only mean
  645. // that the item has been deleted on our side.
  646. ZLOG_DEBUG(m_lpLogger, "ImportFast: %s", "Destination message deleted");
  647. return SYNC_E_OBJECT_DELETED;
  648. }
  649. if (hr == MAPI_E_NOT_FOUND)
  650. hr = ImportMessageCreateAsStream(cValue, lpPropArray, &~ptrMessageImporter);
  651. else
  652. hr = ImportMessageUpdateAsStream(cbEntryId, ptrEntryId, cValue, lpPropArray, &~ptrMessageImporter);
  653. if (hr != hrSuccess) {
  654. if (hr != SYNC_E_IGNORE && hr != SYNC_E_OBJECT_DELETED)
  655. ZLOG_DEBUG(m_lpLogger, "ImportFast: Failed to get MessageImporter, hr = 0x%08x", hr);
  656. return hr;
  657. }
  658. ZLOG_DEBUG(m_lpLogger, "ImportFast: %s", "Wrapping MessageImporter in IStreamAdapter");
  659. hr = ECMessageStreamImporterIStreamAdapter::Create(ptrMessageImporter, &~ptrStream);
  660. if (hr != hrSuccess) {
  661. ZLOG_DEBUG(m_lpLogger, "ImportFast: Failed to wrap message importer, hr = 0x%08x" ,hr);
  662. return hr;
  663. }
  664. *lppStream = ptrStream.release();
  665. return hrSuccess;
  666. }
  667. HRESULT ECExchangeImportContentsChanges::ImportMessageCreateAsStream(ULONG cValue, LPSPropValue lpPropArray, WSMessageStreamImporter **lppMessageImporter)
  668. {
  669. HRESULT hr;
  670. ULONG ulNewFlags = 0;
  671. ULONG cbEntryId = 0;
  672. LPENTRYID lpEntryId = NULL;
  673. WSMessageStreamImporterPtr ptrMessageImporter;
  674. if (lpPropArray == NULL || lppMessageImporter == NULL)
  675. return MAPI_E_INVALID_PARAMETER;
  676. auto lpMessageFlags = PCpropFindProp(lpPropArray, cValue, PR_MESSAGE_FLAGS);
  677. auto lpMessageAssociated = PCpropFindProp(lpPropArray, cValue, PR_ASSOCIATED);
  678. auto lpPropEntryId = PCpropFindProp(lpPropArray, cValue, PR_ENTRYID);
  679. if ((lpMessageFlags != NULL && (lpMessageFlags->Value.ul & MSGFLAG_ASSOCIATED)) || (lpMessageAssociated != NULL && lpMessageAssociated->Value.b))
  680. ulNewFlags = MAPI_ASSOCIATED;
  681. if (lpPropEntryId != NULL && HrCompareEntryIdWithStoreGuid(lpPropEntryId->Value.bin.cb, (LPENTRYID)lpPropEntryId->Value.bin.lpb, &m_lpFolder->GetMsgStore()->GetStoreGuid()) == hrSuccess) {
  682. cbEntryId = lpPropEntryId->Value.bin.cb;
  683. lpEntryId = (LPENTRYID)lpPropEntryId->Value.bin.lpb;
  684. } else {
  685. ZLOG_DEBUG(m_lpLogger, "CreateFast: %s", "Creating new entryid");
  686. hr = HrCreateEntryId(m_lpFolder->GetMsgStore()->GetStoreGuid(), MAPI_MESSAGE, &cbEntryId, &lpEntryId);
  687. if (hr != hrSuccess) {
  688. ZLOG_DEBUG(m_lpLogger, "CreateFast: Failed to create entryid, hr = 0x%08x", hr);
  689. return hr;
  690. }
  691. }
  692. hr = m_lpFolder->CreateMessageFromStream(ulNewFlags, m_ulSyncId, cbEntryId, lpEntryId, &~ptrMessageImporter);
  693. if(hr != hrSuccess) {
  694. ZLOG_DEBUG(m_lpLogger, "CreateFast: Failed to create message from stream, hr = 0x%08x", hr);
  695. return hr;
  696. }
  697. *lppMessageImporter = ptrMessageImporter.release();
  698. return hrSuccess;
  699. }
  700. HRESULT ECExchangeImportContentsChanges::ImportMessageUpdateAsStream(ULONG cbEntryId, LPENTRYID lpEntryId, ULONG cValue, LPSPropValue lpPropArray, WSMessageStreamImporter **lppMessageImporter)
  701. {
  702. HRESULT hr;
  703. SPropValuePtr ptrPropPCL;
  704. SPropValuePtr ptrPropCK;
  705. bool bAssociated = false;
  706. SPropValuePtr ptrConflictItems;
  707. WSMessageStreamImporterPtr ptrMessageImporter;
  708. if (lpEntryId == NULL || lpPropArray == NULL || lppMessageImporter == NULL)
  709. return MAPI_E_INVALID_PARAMETER;
  710. hr = m_lpFolder->GetChangeInfo(cbEntryId, lpEntryId, &~ptrPropPCL, &~ptrPropCK);
  711. if (hr != hrSuccess) {
  712. if (hr == MAPI_E_NOT_FOUND) {
  713. // The item was soft-deleted; sourcekey is known, but we cannot open the item. It has therefore been deleted.
  714. ZLOG_DEBUG(m_lpLogger, "UpdateFast: %s", "The destination item was deleted");
  715. hr = SYNC_E_OBJECT_DELETED;
  716. } else
  717. ZLOG_DEBUG(m_lpLogger, "UpdateFast: Failed to get change info, hr = 0x%08x", hr);
  718. return hr;
  719. }
  720. auto lpRemoteCK = PCpropFindProp(lpPropArray, cValue, PR_CHANGE_KEY);
  721. if (IsProcessed(lpRemoteCK, ptrPropPCL)) {
  722. //we already have this change
  723. ZLOG_DEBUG(m_lpLogger, "UpdateFast: %s", "The item was previously synchronized");
  724. return SYNC_E_IGNORE;
  725. }
  726. auto lpMessageFlags = PCpropFindProp(lpPropArray, cValue, PR_MESSAGE_FLAGS);
  727. auto lpMessageAssociated = PCpropFindProp(lpPropArray, cValue, PR_ASSOCIATED);
  728. if ((lpMessageFlags != NULL && (lpMessageFlags->Value.ul & MSGFLAG_ASSOCIATED)) || (lpMessageAssociated != NULL && lpMessageAssociated->Value.b))
  729. bAssociated = true;
  730. auto lpRemotePCL = PCpropFindProp(lpPropArray, cValue, PR_PREDECESSOR_CHANGE_LIST);
  731. if (!bAssociated && IsConflict(ptrPropCK, lpRemotePCL)) {
  732. MessagePtr ptrMessage;
  733. ULONG ulType = 0;
  734. ZLOG_DEBUG(m_lpLogger, "UpdateFast: %s", "The item seems to be in conflict");
  735. hr = m_lpFolder->OpenEntry(cbEntryId, lpEntryId, &ptrMessage.iid(), MAPI_MODIFY, &ulType, &~ptrMessage);
  736. if (hr == MAPI_E_NOT_FOUND) {
  737. // This shouldn't happen as we just got a conflict.
  738. ZLOG_DEBUG(m_lpLogger, "UpdateFast: %s", "The destination item seems to have disappeared");
  739. return SYNC_E_OBJECT_DELETED;
  740. } else if (hr != hrSuccess) {
  741. ZLOG_DEBUG(m_lpLogger, "UpdateFast: Failed to open conflicting message, hr = 0x%08x", hr);
  742. return hr;
  743. }
  744. if (CreateConflictMessageOnly(ptrMessage, &~ptrConflictItems) == MAPI_E_NOT_FOUND) {
  745. CreateConflictFolders();
  746. CreateConflictMessageOnly(ptrMessage, &~ptrConflictItems);
  747. }
  748. }
  749. hr = m_lpFolder->UpdateMessageFromStream(m_ulSyncId, cbEntryId, lpEntryId, ptrConflictItems, &~ptrMessageImporter);
  750. if (hr != hrSuccess) {
  751. ZLOG_DEBUG(m_lpLogger, "UpdateFast: Failed to update message from stream, hr = 0x%08x", hr);
  752. return hr;
  753. }
  754. *lppMessageImporter = ptrMessageImporter.release();
  755. return hrSuccess;
  756. }
  757. HRESULT ECExchangeImportContentsChanges::SetMessageInterface(REFIID refiid)
  758. {
  759. m_iidMessage = refiid;
  760. return hrSuccess;
  761. }
  762. /**
  763. * Check if the passed entryids can be found in the RES_PROPERTY restrictions with the proptag
  764. * set to PR_PARENT_ENTRYID at any level in the passed restriction.
  765. *
  766. * @param[in] lpRestriction The restriction in which to look for the entryids.
  767. * @param[in,out] lstEntryIds The list of entryids to find. If an entryid is found it
  768. * will be removed from the list.
  769. *
  770. * @retval hrSuccess All entries from the list are found. The list will be empty on exit.
  771. * @retval MAPI_E_NOT_FOUND Not all entries from the list were found.
  772. */
  773. static HRESULT HrRestrictionContains(const SRestriction *lpRestriction,
  774. std::list<SBinary> &lstEntryIds)
  775. {
  776. HRESULT hr = MAPI_E_NOT_FOUND;
  777. switch (lpRestriction->rt) {
  778. case RES_AND:
  779. for (ULONG i = 0; hr != hrSuccess && i < lpRestriction->res.resAnd.cRes; ++i)
  780. hr = HrRestrictionContains(&lpRestriction->res.resAnd.lpRes[i], lstEntryIds);
  781. break;
  782. case RES_OR:
  783. for (ULONG i = 0; hr != hrSuccess && i < lpRestriction->res.resOr.cRes; ++i)
  784. hr = HrRestrictionContains(&lpRestriction->res.resOr.lpRes[i], lstEntryIds);
  785. break;
  786. case RES_NOT:
  787. return HrRestrictionContains(lpRestriction->res.resNot.lpRes, lstEntryIds);
  788. case RES_PROPERTY:
  789. if (lpRestriction->res.resProperty.ulPropTag == PR_PARENT_ENTRYID) {
  790. for (auto i = lstEntryIds.begin(); i != lstEntryIds.cend(); ++i) {
  791. if (Util::CompareSBinary(lpRestriction->res.resProperty.lpProp->Value.bin, *i) == 0) {
  792. lstEntryIds.erase(i);
  793. break;
  794. }
  795. }
  796. if (lstEntryIds.empty())
  797. hr = hrSuccess;
  798. }
  799. break;
  800. default:
  801. break;
  802. }
  803. return hr;
  804. }
  805. /**
  806. * Check if the restriction passed in lpRestriction contains the three conflict
  807. * folders as specified in lpAdditionalREN. If either of the three entryids in
  808. * lpAdditionalREN is empty, the restriction won't be checked and it will be assumed
  809. * to be valid.
  810. *
  811. * @param[in] lpRestriction The restriction that is to be verified.
  812. * @param[in] lpAdditionalREN A MV_BINARY property that contains the entryids of the three
  813. * three conflict folders.
  814. *
  815. * @retval hrSuccess The restriction is valid for the passed AdditionalREN. This means that
  816. * it either contains the three entryids or that at least one of the entryids
  817. * is empty.
  818. * @retval MAPI_E_NOT_FOUND lpAdditionalREN contains all three entryids, but not all of them
  819. * were found in lpRestriction.
  820. */
  821. static HRESULT
  822. HrVerifyRemindersRestriction(const SRestriction *lpRestriction,
  823. const SPropValue *lpAdditionalREN)
  824. {
  825. std::list<SBinary> lstEntryIds;
  826. if (lpAdditionalREN->Value.MVbin.lpbin[0].cb == 0 || lpAdditionalREN->Value.MVbin.lpbin[2].cb == 0 || lpAdditionalREN->Value.MVbin.lpbin[3].cb == 0)
  827. return hrSuccess;
  828. lstEntryIds.push_back(lpAdditionalREN->Value.MVbin.lpbin[0]);
  829. lstEntryIds.push_back(lpAdditionalREN->Value.MVbin.lpbin[2]);
  830. lstEntryIds.push_back(lpAdditionalREN->Value.MVbin.lpbin[3]);
  831. return HrRestrictionContains(lpRestriction, lstEntryIds);
  832. }
  833. HRESULT ECExchangeImportContentsChanges::HrUpdateSearchReminders(LPMAPIFOLDER lpRootFolder,
  834. const SPropValue *lpAdditionalREN)
  835. {
  836. HRESULT hr;
  837. ULONG cREMProps;
  838. SPropArrayPtr ptrREMProps;
  839. LPSPropValue lpREMEntryID = NULL;
  840. MAPIFolderPtr ptrRemindersFolder;
  841. ULONG ulType = 0;
  842. SRestrictionPtr ptrOrigRestriction;
  843. EntryListPtr ptrOrigContainerList;
  844. ULONG ulOrigSearchState = 0;
  845. SRestrictionPtr ptrPreRestriction;
  846. ECAndRestriction resPre;
  847. SPropValue sPropValConflicts = {PR_PARENT_ENTRYID, 0};
  848. SPropValue sPropValLocalFailures = {PR_PARENT_ENTRYID, 0};
  849. SPropValue sPropValServerFailures = {PR_PARENT_ENTRYID, 0};
  850. static constexpr const SizedSPropTagArray(2, sptaREMProps) =
  851. {2, {PR_REM_ONLINE_ENTRYID, PR_REM_OFFLINE_ENTRYID}};
  852. hr = lpRootFolder->GetProps(sptaREMProps, 0, &cREMProps, &~ptrREMProps);
  853. if (FAILED(hr))
  854. return hr;
  855. // Find the correct reminders folder.
  856. if (PROP_TYPE(ptrREMProps[1].ulPropTag) != PT_ERROR)
  857. lpREMEntryID = &ptrREMProps[1];
  858. else if (PROP_TYPE(ptrREMProps[0].ulPropTag) != PT_ERROR)
  859. lpREMEntryID = &ptrREMProps[0];
  860. else
  861. return MAPI_E_NOT_FOUND;
  862. hr = lpRootFolder->OpenEntry(lpREMEntryID->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpREMEntryID->Value.bin.lpb), &ptrRemindersFolder.iid(), MAPI_BEST_ACCESS, &ulType, &~ptrRemindersFolder);
  863. if (hr != hrSuccess)
  864. return hr;
  865. hr = ptrRemindersFolder->GetSearchCriteria(0, &~ptrOrigRestriction, &~ptrOrigContainerList, &ulOrigSearchState);
  866. if (hr != hrSuccess)
  867. return hr;
  868. // First check if the SearchCriteria needs updating by seeing if we can find the restrictions that
  869. // contain the entryids of the folders to exclude. We assume that when they're found they're used
  870. // as expected: as excludes.
  871. hr = HrVerifyRemindersRestriction(ptrOrigRestriction, lpAdditionalREN);
  872. if (hr == hrSuccess)
  873. return hr;
  874. sPropValConflicts.Value.bin = lpAdditionalREN->Value.MVbin.lpbin[0];
  875. sPropValLocalFailures.Value.bin = lpAdditionalREN->Value.MVbin.lpbin[2];
  876. sPropValServerFailures.Value.bin = lpAdditionalREN->Value.MVbin.lpbin[3];
  877. resPre +=
  878. ECPropertyRestriction(RELOP_NE, PR_PARENT_ENTRYID, &sPropValConflicts, ECRestriction::Cheap) +
  879. ECPropertyRestriction(RELOP_NE, PR_PARENT_ENTRYID, &sPropValLocalFailures, ECRestriction::Cheap) +
  880. ECPropertyRestriction(RELOP_NE, PR_PARENT_ENTRYID, &sPropValServerFailures, ECRestriction::Cheap) +
  881. ECRawRestriction(ptrOrigRestriction.get(), ECRestriction::Cheap);
  882. hr = resPre.CreateMAPIRestriction(&~ptrPreRestriction, ECRestriction::Cheap);
  883. if (hr != hrSuccess)
  884. return hr;
  885. return ptrRemindersFolder->SetSearchCriteria(ptrPreRestriction, ptrOrigContainerList, RESTART_SEARCH | (ulOrigSearchState & (SEARCH_FOREGROUND | SEARCH_RECURSIVE)));
  886. }
  887. DEF_ULONGMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, AddRef, (void))
  888. DEF_ULONGMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, Release, (void))
  889. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, QueryInterface, (REFIID, refiid), (void **, lppInterface))
  890. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, GetLastError, (HRESULT, hError), (ULONG, ulFlags), (LPMAPIERROR *, lppMapiError))
  891. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, Config, (LPSTREAM, lpStream), (ULONG, ulFlags))
  892. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, UpdateState, (LPSTREAM, lpStream))
  893. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, ImportMessageChange, (ULONG, cValue), (LPSPropValue, lpPropArray), (ULONG, ulFlags), (LPMESSAGE *, lppMessage))
  894. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, ImportMessageDeletion, (ULONG, ulFlags), (LPENTRYLIST, lpSourceEntryList))
  895. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, ImportPerUserReadStateChange, (ULONG, cElements), (LPREADSTATE, lpReadState))
  896. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, ImportMessageMove, (ULONG, cbSourceKeySrcFolder), (BYTE *, pbSourceKeySrcFolder), (ULONG, cbSourceKeySrcMessage), (BYTE *, pbSourceKeySrcMessage), (ULONG, cbPCLMessage), (BYTE *, pbPCLMessage), (ULONG, cbSourceKeyDestMessage), (BYTE *, pbSourceKeyDestMessage), (ULONG, cbChangeNumDestMessage), (BYTE *, pbChangeNumDestMessage))
  897. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, ConfigForConversionStream, (LPSTREAM, lpStream), (ULONG, ulFlags), (ULONG, cValuesConversion), (LPSPropValue, lpPropArrayConversion))
  898. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, ImportMessageChangeAsAStream, (ULONG, cpvalChanges), (LPSPropValue, ppvalChanges), (ULONG, ulFlags), (LPSTREAM *, lppstream))
  899. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeImportContentsChanges, ECImportContentsChanges, SetMessageInterface, (REFIID, refiid))