ECExchangeExportChanges.cpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398
  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 <kopano/mapi_ptr.h>
  22. #include "ECExchangeExportChanges.h"
  23. #include "WSMessageStreamExporter.h"
  24. #include "WSSerializedMessage.h"
  25. #include <set>
  26. #include <kopano/Util.h>
  27. #include <kopano/ECGuid.h>
  28. #include <edkguid.h>
  29. #include <mapiguid.h>
  30. #include <kopano/mapiext.h>
  31. #include <mapiutil.h>
  32. #include "ics.h"
  33. #include <kopano/ECDebug.h>
  34. #include "Mem.h"
  35. #include "ECMessage.h"
  36. #include <kopano/stringutil.h>
  37. #include "ECSyncLog.h"
  38. #include "ECSyncUtil.h"
  39. #include "ECSyncSettings.h"
  40. #include "EntryPoint.h"
  41. #include <kopano/CommonUtil.h>
  42. // We use ntohl/htonl for network-order conversion
  43. #include <arpa/inet.h>
  44. #include <kopano/charset/convert.h>
  45. using namespace KCHL;
  46. ECExchangeExportChanges::ECExchangeExportChanges(ECMsgStore *lpStore, const std::string &sk, const wchar_t * szDisplay, unsigned int ulSyncType)
  47. : m_iidMessage(IID_IMessage)
  48. {
  49. ECSyncLog::GetLogger(&m_lpLogger);
  50. m_lpStore = lpStore;
  51. m_sourcekey = sk;
  52. m_strDisplay = szDisplay ? szDisplay : L"<Unknown>";
  53. m_ulSyncType = ulSyncType;
  54. // In server-side sync, only use a batch size of 1.
  55. if (m_sourcekey.empty())
  56. m_ulBatchSize = 1;
  57. else
  58. m_ulBatchSize = 256;
  59. memset(&m_tmsStart, 0, sizeof(m_tmsStart));
  60. m_lpStore->AddRef();
  61. }
  62. ECExchangeExportChanges::~ECExchangeExportChanges(){
  63. MAPIFreeBuffer(m_lpChanges);
  64. if(m_lpStore)
  65. m_lpStore->Release();
  66. if(m_lpStream)
  67. m_lpStream->Release();
  68. if(m_lpImportContents)
  69. m_lpImportContents->Release();
  70. if(m_lpImportStreamedContents)
  71. m_lpImportStreamedContents->Release();
  72. if(m_lpImportHierarchy)
  73. m_lpImportHierarchy->Release();
  74. MAPIFreeBuffer(m_lpRestrict);
  75. if(m_lpLogger)
  76. m_lpLogger->Release();
  77. }
  78. HRESULT ECExchangeExportChanges::SetLogger(ECLogger *lpLogger)
  79. {
  80. if(m_lpLogger)
  81. m_lpLogger->Release();
  82. m_lpLogger = lpLogger;
  83. if (m_lpLogger != NULL)
  84. m_lpLogger->AddRef();
  85. return hrSuccess;
  86. }
  87. HRESULT ECExchangeExportChanges::Create(ECMsgStore *lpStore, REFIID iid, const std::string& sourcekey, const wchar_t *szDisplay, unsigned int ulSyncType, LPEXCHANGEEXPORTCHANGES* lppExchangeExportChanges){
  88. if (lpStore == NULL || (ulSyncType != ICS_SYNC_CONTENTS && ulSyncType != ICS_SYNC_HIERARCHY))
  89. return MAPI_E_INVALID_PARAMETER;
  90. return alloc_wrap<ECExchangeExportChanges>(lpStore, sourcekey,
  91. szDisplay, ulSyncType).as(iid, lppExchangeExportChanges);
  92. }
  93. HRESULT ECExchangeExportChanges::QueryInterface(REFIID refiid, void **lppInterface)
  94. {
  95. REGISTER_INTERFACE2(ECExchangeExportChanges, this);
  96. REGISTER_INTERFACE2(ECUnknown, this);
  97. REGISTER_INTERFACE2(IExchangeExportChanges, &this->m_xECExportChanges);
  98. REGISTER_INTERFACE2(IUnknown, &this->m_xECExportChanges);
  99. REGISTER_INTERFACE2(IECExportChanges, &this->m_xECExportChanges);
  100. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  101. }
  102. HRESULT ECExchangeExportChanges::GetLastError(HRESULT hResult, ULONG ulFlags, LPMAPIERROR *lppMAPIError){
  103. HRESULT hr = hrSuccess;
  104. ecmem_ptr<MAPIERROR> lpMapiError;
  105. memory_ptr<TCHAR> lpszErrorMsg;
  106. //FIXME: give synchronization errors messages
  107. hr = Util::HrMAPIErrorToText((hResult == hrSuccess)?MAPI_E_NO_ACCESS : hResult, &~lpszErrorMsg);
  108. if (hr != hrSuccess)
  109. return hr;
  110. hr = ECAllocateBuffer(sizeof(MAPIERROR), &~lpMapiError);
  111. if(hr != hrSuccess)
  112. return hr;
  113. if (ulFlags & MAPI_UNICODE) {
  114. std::wstring wstrErrorMsg = convert_to<std::wstring>(lpszErrorMsg.get());
  115. std::wstring wstrCompName = convert_to<std::wstring>(g_strProductName.c_str());
  116. if ((hr = MAPIAllocateMore(sizeof(std::wstring::value_type) * (wstrErrorMsg.size() + 1), lpMapiError, (void**)&lpMapiError->lpszError)) != hrSuccess)
  117. return hr;
  118. wcscpy((wchar_t*)lpMapiError->lpszError, wstrErrorMsg.c_str());
  119. if ((hr = MAPIAllocateMore(sizeof(std::wstring::value_type) * (wstrCompName.size() + 1), lpMapiError, (void**)&lpMapiError->lpszComponent)) != hrSuccess)
  120. return hr;
  121. wcscpy((wchar_t*)lpMapiError->lpszComponent, wstrCompName.c_str());
  122. } else {
  123. std::string strErrorMsg = convert_to<std::string>(lpszErrorMsg.get());
  124. std::string strCompName = convert_to<std::string>(g_strProductName.c_str());
  125. if ((hr = MAPIAllocateMore(strErrorMsg.size() + 1, lpMapiError, (void**)&lpMapiError->lpszError)) != hrSuccess)
  126. return hr;
  127. strcpy((char*)lpMapiError->lpszError, strErrorMsg.c_str());
  128. if ((hr = MAPIAllocateMore(strCompName.size() + 1, lpMapiError, (void**)&lpMapiError->lpszComponent)) != hrSuccess)
  129. return hr;
  130. strcpy((char*)lpMapiError->lpszComponent, strCompName.c_str());
  131. }
  132. lpMapiError->ulContext = 0;
  133. lpMapiError->ulLowLevelError= 0;
  134. lpMapiError->ulVersion = 0;
  135. *lppMAPIError = lpMapiError.release();
  136. return hrSuccess;
  137. }
  138. HRESULT ECExchangeExportChanges::Config(LPSTREAM lpStream, ULONG ulFlags, LPUNKNOWN lpCollector, LPSRestriction lpRestriction, LPSPropTagArray lpIncludeProps, LPSPropTagArray lpExcludeProps, ULONG ulBufferSize){
  139. HRESULT hr;
  140. ULONG ulSyncId = 0;
  141. ULONG ulChangeId = 0;
  142. ULONG ulStep = 0;
  143. BOOL bCanStream = FALSE;
  144. bool bForceImplicitStateUpdate = false;
  145. ECSyncSettings *lpSyncSettings = ECSyncSettings::GetInstance();
  146. typedef std::map<SBinary, ChangeListIter, Util::SBinaryLess> ChangeMap;
  147. typedef ChangeMap::iterator ChangeMapIter;
  148. ChangeMap mapChanges;
  149. ChangeMapIter iterLastChange;
  150. ChangeList lstChange;
  151. std::string sourcekey;
  152. if(m_bConfiged){
  153. ZLOG_DEBUG(m_lpLogger, "Config() called twice");
  154. return MAPI_E_UNCONFIGURED;
  155. }
  156. if(lpRestriction) {
  157. hr = Util::HrCopySRestriction(&m_lpRestrict, lpRestriction);
  158. if(hr != hrSuccess) {
  159. ZLOG_DEBUG(m_lpLogger, "Invalid restriction");
  160. return hr;
  161. }
  162. } else {
  163. m_lpRestrict = NULL;
  164. }
  165. m_ulFlags = ulFlags;
  166. if(! (ulFlags & SYNC_CATCHUP)) {
  167. if(lpCollector == NULL) {
  168. ZLOG_DEBUG(m_lpLogger, "No importer to export to");
  169. return MAPI_E_INVALID_PARAMETER;
  170. }
  171. // We don't need the importer when doing SYNC_CATCHUP
  172. if(m_ulSyncType == ICS_SYNC_CONTENTS){
  173. hr = lpCollector->QueryInterface(IID_IExchangeImportContentsChanges, (LPVOID*) &m_lpImportContents);
  174. if (hr == hrSuccess && lpSyncSettings->SyncStreamEnabled()) {
  175. m_lpStore->lpTransport->HrCheckCapabilityFlags(KOPANO_CAP_ENHANCED_ICS, &bCanStream);
  176. if (bCanStream == TRUE) {
  177. ZLOG_DEBUG(m_lpLogger, "Exporter supports enhanced ICS, checking importer...");
  178. hr = lpCollector->QueryInterface(IID_IECImportContentsChanges, (LPVOID*) &m_lpImportStreamedContents);
  179. if (hr == MAPI_E_INTERFACE_NOT_SUPPORTED) {
  180. assert(m_lpImportStreamedContents == NULL);
  181. hr = hrSuccess;
  182. ZLOG_DEBUG(m_lpLogger, "Importer doesn't support enhanced ICS");
  183. } else
  184. ZLOG_DEBUG(m_lpLogger, "Importer supports enhanced ICS");
  185. } else
  186. ZLOG_DEBUG(m_lpLogger, "Exporter doesn't support enhanced ICS");
  187. }
  188. }else if(m_ulSyncType == ICS_SYNC_HIERARCHY){
  189. hr = lpCollector->QueryInterface(IID_IExchangeImportHierarchyChanges, (LPVOID*) &m_lpImportHierarchy);
  190. }else{
  191. hr = MAPI_E_INVALID_PARAMETER;
  192. }
  193. if(hr != hrSuccess)
  194. return hr;
  195. }
  196. if (lpStream == NULL){
  197. LARGE_INTEGER lint = {{ 0, 0 }};
  198. ULONG tmp[2] = { 0, 0 };
  199. ULONG ulSize = 0;
  200. ZLOG_DEBUG(m_lpLogger, "Creating new exporter stream");
  201. hr = CreateStreamOnHGlobal(GlobalAlloc(GPTR, sizeof(tmp)), true, &m_lpStream);
  202. if (hr != hrSuccess) {
  203. ZLOG_DEBUG(m_lpLogger, "Unable to create new exporter stream");
  204. return hr;
  205. }
  206. m_lpStream->Seek(lint, STREAM_SEEK_SET, NULL);
  207. m_lpStream->Write(tmp, sizeof(tmp), &ulSize);
  208. } else {
  209. hr = lpStream->QueryInterface(IID_IStream, (LPVOID*)&m_lpStream);
  210. if (hr != hrSuccess) {
  211. ZLOG_DEBUG(m_lpLogger, "Passed state stream does not support IStream interface");
  212. return hr;
  213. }
  214. }
  215. hr = HrDecodeSyncStateStream(m_lpStream, &ulSyncId, &ulChangeId, &m_setProcessedChanges);
  216. if(hr != hrSuccess) {
  217. ZLOG_DEBUG(m_lpLogger, "Unable to decode sync state stream, hr=0x%08x", hr);
  218. return hr;
  219. }
  220. ZLOG_DEBUG(m_lpLogger, "Decoded state stream: syncid=%u, changeid=%u, processed changes=%lu", ulSyncId, ulChangeId, (long unsigned int)m_setProcessedChanges.size());
  221. if(ulSyncId == 0) {
  222. ZLOG_DEBUG(m_lpLogger, "Getting new sync id for %s folder '%ls'...", (m_lpStore->IsOfflineStore() ? "offline" : "online"), m_strDisplay.c_str());
  223. // Ignore any trailing garbage in the stream
  224. if (m_sourcekey.size() < 24 && (m_sourcekey[m_sourcekey.size()-1] & 0x80)) {
  225. // If we're getting a truncated sourcekey, untruncate it now so we can pass it to GetChanges()
  226. sourcekey = m_sourcekey;
  227. sourcekey[m_sourcekey.size()-1] &= ~0x80;
  228. sourcekey.append(1, '\x00');
  229. sourcekey.append(1, '\x00');
  230. } else {
  231. sourcekey = m_sourcekey;
  232. }
  233. // Register our sync with the server, get a sync ID
  234. hr = m_lpStore->lpTransport->HrSetSyncStatus(sourcekey, ulSyncId, ulChangeId, m_ulSyncType, 0, &ulSyncId);
  235. if(hr != hrSuccess) {
  236. ZLOG_DEBUG(m_lpLogger, "Unable to update sync status on server, hr=0x%08x", hr);
  237. return hr;
  238. }
  239. ZLOG_DEBUG(m_lpLogger, "New sync id for %s folder '%ls': %u", (m_lpStore->IsOfflineStore() ? "offline" : "online"), m_strDisplay.c_str(), ulSyncId);
  240. bForceImplicitStateUpdate = true;
  241. }
  242. MAPIFreeBuffer(m_lpChanges);
  243. m_lpChanges = NULL;
  244. hr = m_lpStore->lpTransport->HrGetChanges(sourcekey, ulSyncId, ulChangeId, m_ulSyncType, ulFlags, m_lpRestrict, &m_ulMaxChangeId, &m_ulChanges, &m_lpChanges);
  245. if(hr != hrSuccess) {
  246. ZLOG_DEBUG(m_lpLogger, "Unable to get changes from server, hr=0x%08x", hr);
  247. return hr;
  248. }
  249. m_ulSyncId = ulSyncId;
  250. m_ulChangeId = ulChangeId;
  251. m_lpLogger->Log(EC_LOGLEVEL_INFO, "%s folder: %ls changes: %u syncid: %u changeid: %u", (m_lpStore->IsOfflineStore() ? "offline" : "online"), m_strDisplay.c_str(), m_ulChanges, m_ulSyncId, m_ulChangeId);
  252. /**
  253. * Filter the changes.
  254. * How this works:
  255. * If no previous change has been seen for a particular sourcekey, the change will just be added to
  256. * the correct list. The iterator into that list is stored in a map, keyed by the sourcekey.
  257. * If a previous change is found, action is taken based on the type of that change. The action can
  258. * involve removing the change from a change list and/or adding it to another list.
  259. *
  260. * Note that lstChange is a local list, which is copied into m_lstChange after filtering. This is
  261. * done because m_lstChange is not a list but a vector, which is not well suited for removing
  262. * items in the middle of the data.
  263. */
  264. for (ulStep = 0; ulStep < m_ulChanges; ++ulStep) {
  265. // First check if this change hasn't been processed yet
  266. if (m_setProcessedChanges.find(std::pair<unsigned int, std::string>(m_lpChanges[ulStep].ulChangeId, std::string((char *)m_lpChanges[ulStep].sSourceKey.lpb, m_lpChanges[ulStep].sSourceKey.cb))) != m_setProcessedChanges.end())
  267. continue;
  268. iterLastChange = mapChanges.find(m_lpChanges[ulStep].sSourceKey);
  269. if (iterLastChange == mapChanges.end()) {
  270. switch (ICS_ACTION(m_lpChanges[ulStep].ulChangeType)) {
  271. case ICS_NEW:
  272. case ICS_CHANGE:
  273. mapChanges.insert(ChangeMap::value_type(m_lpChanges[ulStep].sSourceKey, lstChange.insert(lstChange.end(), m_lpChanges[ulStep])));
  274. break;
  275. case ICS_FLAG:
  276. mapChanges.insert(ChangeMap::value_type(m_lpChanges[ulStep].sSourceKey, m_lstFlag.insert(m_lstFlag.end(), m_lpChanges[ulStep])));
  277. break;
  278. case ICS_SOFT_DELETE:
  279. mapChanges.insert(ChangeMap::value_type(m_lpChanges[ulStep].sSourceKey, m_lstSoftDelete.insert(m_lstSoftDelete.end(), m_lpChanges[ulStep])));
  280. break;
  281. case ICS_HARD_DELETE:
  282. mapChanges.insert(ChangeMap::value_type(m_lpChanges[ulStep].sSourceKey, m_lstHardDelete.insert(m_lstHardDelete.end(), m_lpChanges[ulStep])));
  283. break;
  284. default:
  285. break;
  286. }
  287. } else {
  288. switch (ICS_ACTION(m_lpChanges[ulStep].ulChangeType)) {
  289. case ICS_NEW:
  290. // This shouldn't happen since apparently we have another change for the same object.
  291. // However, if an object gets moved to another folder and back, we'll get a delete followed by an add. If that happens
  292. // we skip the delete and morph the current add to a change.
  293. if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_SOFT_DELETE || ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_HARD_DELETE) {
  294. ChangeListIter iterNewChange = lstChange.insert(lstChange.end(), *iterLastChange->second);
  295. iterNewChange->ulChangeType = (iterNewChange->ulChangeType & ~ICS_ACTION_MASK) | ICS_CHANGE;
  296. if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_SOFT_DELETE)
  297. m_lstSoftDelete.erase(iterLastChange->second);
  298. else
  299. m_lstHardDelete.erase(iterLastChange->second);
  300. iterLastChange->second = iterNewChange;
  301. ZLOG_DEBUG(m_lpLogger, "Got an ICS_NEW change for a previously deleted object. I converted it to a change. sourcekey=%s", bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  302. } else
  303. ZLOG_DEBUG(m_lpLogger, "Got an ICS_NEW change for an object we've seen before. prev_change=%04x, sourcekey=%s", iterLastChange->second->ulChangeType, bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  304. break;
  305. case ICS_CHANGE:
  306. // Any change is allowed as the previous change except a change.
  307. if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_CHANGE)
  308. ZLOG_DEBUG(m_lpLogger, "Got an ICS_CHANGE on an object for which we just saw an ICS_CHANGE. sourcekey=%s", bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  309. // A previous delete is allowed for the same reason as in ICS_NEW.
  310. if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_SOFT_DELETE || ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_HARD_DELETE) {
  311. if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_SOFT_DELETE)
  312. m_lstSoftDelete.erase(iterLastChange->second);
  313. else
  314. m_lstHardDelete.erase(iterLastChange->second);
  315. iterLastChange->second = lstChange.insert(lstChange.end(), m_lpChanges[ulStep]);
  316. ZLOG_DEBUG(m_lpLogger, "Got an ICS_CHANGE change for a previously deleted object. sourcekey=%s", bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  317. } else if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_FLAG) {
  318. m_lstFlag.erase(iterLastChange->second);
  319. iterLastChange->second = lstChange.insert(lstChange.end(), m_lpChanges[ulStep]);
  320. ZLOG_DEBUG(m_lpLogger, "Upgraded a previous ICS_FLAG to ICS_CHANGED. sourcekey=%s", bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  321. } else
  322. ZLOG_DEBUG(m_lpLogger, "Ignoring ICS_CHANGE due to a previous change. prev_change=%04x, sourcekey=%s", iterLastChange->second->ulChangeType, bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  323. break;
  324. case ICS_FLAG:
  325. // This is only allowed after an ICS_NEW and ICS_CHANGE. It will be ignored in any case.
  326. if (ICS_ACTION(iterLastChange->second->ulChangeType) != ICS_NEW && ICS_ACTION(iterLastChange->second->ulChangeType) != ICS_CHANGE)
  327. ZLOG_DEBUG(m_lpLogger, "Got an ICS_FLAG with something else than a ICS_NEW or ICS_CHANGE as the previous changes. prev_change=%04x, sourcekey=%s", iterLastChange->second->ulChangeType, bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  328. ZLOG_DEBUG(m_lpLogger, "Ignoring ICS_FLAG due to previous ICS_NEW or ICS_CHANGE. prev_change=%04x, sourcekey=%s", iterLastChange->second->ulChangeType, bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  329. break;
  330. case ICS_SOFT_DELETE:
  331. case ICS_HARD_DELETE:
  332. // We'll ignore the previous change and replace it with this delete. We won't write it now as
  333. // we could get an add for the same object. But because of the reordering (deletes after all adds/changes) we would delete
  334. // the new object. Therefore we'll make a change out of a delete - add/change.
  335. ZLOG_DEBUG(m_lpLogger, "Replacing previous change with current ICS_xxxx_DELETE. prev_change=%04x, sourcekey=%s", iterLastChange->second->ulChangeType, bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  336. if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_NEW || ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_CHANGE)
  337. lstChange.erase(iterLastChange->second);
  338. else if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_FLAG)
  339. m_lstFlag.erase(iterLastChange->second);
  340. else if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_SOFT_DELETE)
  341. m_lstSoftDelete.erase(iterLastChange->second);
  342. else if (ICS_ACTION(iterLastChange->second->ulChangeType) == ICS_HARD_DELETE)
  343. m_lstHardDelete.erase(iterLastChange->second);
  344. if (ICS_ACTION(m_lpChanges[ulStep].ulChangeType) == ICS_SOFT_DELETE)
  345. iterLastChange->second = m_lstSoftDelete.insert(m_lstSoftDelete.end(), m_lpChanges[ulStep]);
  346. else
  347. iterLastChange->second = m_lstHardDelete.insert(m_lstHardDelete.end(), m_lpChanges[ulStep]);
  348. break;
  349. default:
  350. ZLOG_DEBUG(m_lpLogger, "Got an unknown change. change=%04x, sourcekey=%s", m_lpChanges[ulStep].ulChangeType, bin2hex(m_lpChanges[ulStep].sSourceKey.cb, m_lpChanges[ulStep].sSourceKey.lpb).c_str());
  351. break;
  352. }
  353. }
  354. }
  355. m_lstChange.assign(lstChange.begin(), lstChange.end());
  356. if(ulBufferSize != 0){
  357. m_ulBufferSize = ulBufferSize;
  358. }else{
  359. m_ulBufferSize = 10;
  360. }
  361. m_bConfiged = true;
  362. if (bForceImplicitStateUpdate) {
  363. ZLOG_DEBUG(m_lpLogger, "Forcing state update for %s folder '%ls'", (m_lpStore->IsOfflineStore() ? "offline" : "online"), m_strDisplay.c_str());
  364. if (m_ulChangeId == 0)
  365. UpdateState(NULL);
  366. }
  367. return hrSuccess;
  368. }
  369. HRESULT ECExchangeExportChanges::Synchronize(ULONG *lpulSteps, ULONG *lpulProgress)
  370. {
  371. HRESULT hr = hrSuccess;
  372. if(!m_bConfiged){
  373. ZLOG_DEBUG(m_lpLogger, "Config() not called before Synchronize()");
  374. return MAPI_E_UNCONFIGURED;
  375. }
  376. if(m_ulFlags & SYNC_CATCHUP){
  377. m_ulChangeId = m_ulMaxChangeId > m_ulChangeId ? m_ulMaxChangeId : m_ulChangeId;
  378. hr = UpdateStream(m_lpStream);
  379. if (hr == hrSuccess)
  380. *lpulProgress = *lpulSteps = 0;
  381. return hr;
  382. }
  383. if (*lpulProgress == 0 && m_lpLogger->Log(EC_LOGLEVEL_DEBUG))
  384. m_clkStart = times(&m_tmsStart);
  385. if(m_ulSyncType == ICS_SYNC_CONTENTS){
  386. hr = ExportMessageChanges();
  387. if(hr == SYNC_W_PROGRESS)
  388. goto progress;
  389. if(hr != hrSuccess)
  390. return hr;
  391. hr = ExportMessageDeletes();
  392. if(hr != hrSuccess)
  393. return hr;
  394. hr = ExportMessageFlags();
  395. if(hr != hrSuccess)
  396. return hr;
  397. }else if(m_ulSyncType == ICS_SYNC_HIERARCHY){
  398. hr = ExportFolderChanges();
  399. if(hr == SYNC_W_PROGRESS)
  400. goto progress;
  401. if(hr != hrSuccess)
  402. return hr;
  403. hr = ExportFolderDeletes();
  404. if(hr != hrSuccess)
  405. return hr;
  406. }else{
  407. return MAPI_E_INVALID_PARAMETER;
  408. }
  409. hr = UpdateStream(m_lpStream);
  410. if(hr != hrSuccess)
  411. return hr;
  412. if(! (m_ulFlags & SYNC_CATCHUP)) {
  413. if (m_ulSyncType == ICS_SYNC_CONTENTS)
  414. hr = m_lpImportContents->UpdateState(NULL);
  415. else
  416. hr = m_lpImportHierarchy->UpdateState(NULL);
  417. if(hr != hrSuccess) {
  418. ZLOG_DEBUG(m_lpLogger, "Importer state update failed, hr=0x%08x", hr);
  419. return hr;
  420. }
  421. }
  422. progress:
  423. if(hr == hrSuccess) {
  424. // The sync has completed. This means we can move to the next change ID on the server, and clear our
  425. // processed change list. If we can't save the new state the the server, we just keep the previous
  426. // change ID and the (large) change list. This allows us always to have a state, even when we can't
  427. // communicate with the server.
  428. if(m_lpStore->lpTransport->HrSetSyncStatus(m_sourcekey, m_ulSyncId, m_ulMaxChangeId, m_ulSyncType, 0, &m_ulSyncId) == hrSuccess) {
  429. ZLOG_DEBUG(m_lpLogger, "Done: syncid=%u, changeid=%u/%u", m_ulSyncId, m_ulChangeId, m_ulMaxChangeId);
  430. m_ulChangeId = m_ulMaxChangeId;
  431. m_setProcessedChanges.clear();
  432. if(m_ulChanges) {
  433. if (m_lpLogger->Log(EC_LOGLEVEL_DEBUG)) {
  434. struct tms tmsEnd = {0};
  435. clock_t clkEnd = times(&tmsEnd);
  436. double dblDuration = 0;
  437. char szDuration[64] = {0};
  438. // Calculate diff
  439. dblDuration = (double)(clkEnd - m_clkStart) / TICKS_PER_SEC;
  440. if (dblDuration >= 60)
  441. snprintf(szDuration, sizeof(szDuration), "%u:%02u.%03u min.", (unsigned)(dblDuration / 60), (unsigned)dblDuration % 60, (unsigned)(dblDuration * 1000 + .5) % 1000);
  442. else
  443. snprintf(szDuration, sizeof(szDuration), "%u.%03u s.", (unsigned)dblDuration % 60, (unsigned)(dblDuration * 1000 + .5) % 1000);
  444. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "folder changes synchronized in %s", szDuration);
  445. } else
  446. m_lpLogger->Log(EC_LOGLEVEL_INFO, "folder changes synchronized");
  447. }
  448. }
  449. }
  450. *lpulSteps = m_lstChange.size();
  451. *lpulProgress = m_ulStep;
  452. return hr;
  453. }
  454. HRESULT ECExchangeExportChanges::UpdateState(LPSTREAM lpStream){
  455. if(!m_bConfiged){
  456. ZLOG_DEBUG(m_lpLogger, "Config() not called before UpdateState()");
  457. return MAPI_E_UNCONFIGURED;
  458. }
  459. if (lpStream == NULL)
  460. lpStream = m_lpStream;
  461. return UpdateStream(lpStream);
  462. }
  463. HRESULT ECExchangeExportChanges::GetChangeCount(ULONG *lpcChanges) {
  464. ULONG cChanges = 0;
  465. if(!m_bConfiged){
  466. ZLOG_DEBUG(m_lpLogger, "Config() not called before GetChangeCount()");
  467. return MAPI_E_UNCONFIGURED;
  468. }
  469. // Changes in flags or deletions are only one step all together
  470. if(!m_lstHardDelete.empty() || !m_lstSoftDelete.empty() || !m_lstFlag.empty())
  471. ++cChanges;
  472. cChanges += m_lstChange.size();
  473. *lpcChanges = cChanges;
  474. return hrSuccess;
  475. }
  476. /**
  477. * This allows you to configure the exporter for a selective export of messages. Since the caller
  478. * specifies which items to export, it is not an incremental export and it has no state. It will call
  479. * the importer with data for all the specified messages, unless they are unavailable. This means that
  480. * the importer will only receive ImportMessageChange() or ImportMessageChangeAsStream() calls.
  481. *
  482. * @param[in] ulPropTag Property tag of identifiers in lpEntries, either PR_SOURCE_KEY or PR_ENTRYID
  483. * @param[in] lpEntries List of entries to export
  484. * @param[in] lpParents List of parents for entries in lpEntries. Must be the same size as lpEntries. Must be NULL if ulPropTag == PR_ENTRYID.
  485. * @param[in] ulFlags Unused for now
  486. * @param[in] lpCollector Importer to send the data to. Must implement either IExchangeImportContentsChanges or IECImportContentsChanges
  487. * @param[in] lpIncludeProps Unused for now
  488. * @param[in] lpExcludeProps Unused for now
  489. * @param[in] ulBufferSize Number of messages so synchronize during a single Synchronize() call. A value of 0 means 'default', which is 1
  490. * @return result
  491. */
  492. HRESULT ECExchangeExportChanges::ConfigSelective(ULONG ulPropTag, LPENTRYLIST lpEntries, LPENTRYLIST lpParents, ULONG ulFlags, LPUNKNOWN lpCollector, LPSPropTagArray lpIncludeProps, LPSPropTagArray lpExcludeProps, ULONG ulBufferSize)
  493. {
  494. HRESULT hr;
  495. ECSyncSettings *lpSyncSettings = ECSyncSettings::GetInstance();
  496. BOOL bCanStream = false;
  497. BOOL bSupportsPropTag = false;
  498. if (ulPropTag != PR_ENTRYID && ulPropTag != PR_SOURCE_KEY)
  499. return MAPI_E_INVALID_PARAMETER;
  500. if(ulPropTag == PR_ENTRYID) {
  501. m_lpStore->lpTransport->HrCheckCapabilityFlags(KOPANO_CAP_EXPORT_PROPTAG, &bSupportsPropTag);
  502. if (!bSupportsPropTag)
  503. return MAPI_E_NO_SUPPORT;
  504. }
  505. if (ulPropTag == PR_ENTRYID && lpParents != NULL)
  506. return MAPI_E_INVALID_PARAMETER;
  507. if (ulPropTag == PR_SOURCE_KEY && lpParents == NULL)
  508. return MAPI_E_INVALID_PARAMETER;
  509. if (lpParents != NULL && lpParents->cValues != lpEntries->cValues)
  510. return MAPI_E_INVALID_PARAMETER;
  511. if(m_bConfiged){
  512. ZLOG_DEBUG(m_lpLogger, "Config() called twice");
  513. return MAPI_E_UNCONFIGURED;
  514. }
  515. // Only available for message syncing
  516. if (m_ulSyncType != ICS_SYNC_CONTENTS)
  517. return MAPI_E_NO_SUPPORT;
  518. // Select an importer interface
  519. hr = lpCollector->QueryInterface(IID_IExchangeImportContentsChanges, (LPVOID*) &m_lpImportContents);
  520. if (hr == hrSuccess && lpSyncSettings->SyncStreamEnabled()) {
  521. m_lpStore->lpTransport->HrCheckCapabilityFlags(KOPANO_CAP_ENHANCED_ICS, &bCanStream);
  522. if (bCanStream == TRUE) {
  523. ZLOG_DEBUG(m_lpLogger, "Exporter supports enhanced ICS, checking importer...");
  524. hr = lpCollector->QueryInterface(IID_IECImportContentsChanges, (LPVOID*) &m_lpImportStreamedContents);
  525. if (hr == MAPI_E_INTERFACE_NOT_SUPPORTED) {
  526. assert(m_lpImportStreamedContents == NULL);
  527. hr = hrSuccess;
  528. ZLOG_DEBUG(m_lpLogger, "Importer doesn't support enhanced ICS");
  529. } else
  530. ZLOG_DEBUG(m_lpLogger, "Importer supports enhanced ICS");
  531. } else
  532. ZLOG_DEBUG(m_lpLogger, "Exporter doesn't support enhanced ICS");
  533. }
  534. m_ulEntryPropTag = ulPropTag;
  535. // Fill m_lpChanges with items from lpEntries
  536. hr = MAPIAllocateBuffer(sizeof(ICSCHANGE) * lpEntries->cValues, (void **)&m_lpChanges);
  537. if(hr != hrSuccess)
  538. return hr;
  539. for (unsigned int i = 0; i < lpEntries->cValues; ++i) {
  540. memset(&m_lpChanges[i], 0, sizeof(ICSCHANGE));
  541. hr = MAPIAllocateMore(lpEntries->lpbin[i].cb, m_lpChanges, (void **)&m_lpChanges[i].sSourceKey.lpb);
  542. if(hr != hrSuccess)
  543. return hr;
  544. memcpy(m_lpChanges[i].sSourceKey.lpb, lpEntries->lpbin[i].lpb, lpEntries->lpbin[i].cb);
  545. m_lpChanges[i].sSourceKey.cb = lpEntries->lpbin[i].cb;
  546. if(lpParents) {
  547. hr = MAPIAllocateMore(lpParents->lpbin[i].cb, m_lpChanges, (void **)&m_lpChanges[i].sParentSourceKey.lpb);
  548. if(hr != hrSuccess)
  549. return hr;
  550. memcpy(m_lpChanges[i].sParentSourceKey.lpb, lpParents->lpbin[i].lpb, lpParents->lpbin[i].cb);
  551. m_lpChanges[i].sParentSourceKey.cb = lpParents->lpbin[i].cb;
  552. }
  553. m_lpChanges[i].ulChangeType = ICS_MESSAGE_NEW;
  554. // Since all changes are 'change' modifications, duplicate all data in m_lpChanges in m_lstChange
  555. m_lstChange.push_back(m_lpChanges[i]);
  556. }
  557. m_bConfiged = true;
  558. return hrSuccess;
  559. }
  560. HRESULT ECExchangeExportChanges::SetMessageInterface(REFIID refiid) {
  561. m_iidMessage = refiid;
  562. return hrSuccess;
  563. }
  564. DEF_ULONGMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, AddRef, (void))
  565. DEF_ULONGMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, Release, (void))
  566. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, QueryInterface, (REFIID, refiid), (void **, lppInterface))
  567. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, GetLastError, (HRESULT, hError), (ULONG, ulFlags), (LPMAPIERROR *, lppMapiError))
  568. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, Config, (LPSTREAM, lpStream), (ULONG, ulFlags), (LPUNKNOWN, lpCollector), (LPSRestriction, lpRestriction), (LPSPropTagArray, lpIncludeProps), (LPSPropTagArray, lpExcludeProps), (ULONG, ulBufferSize))
  569. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, ConfigSelective, (ULONG, ulPropTag), (LPENTRYLIST, lpEntries), (LPENTRYLIST, lpParents), (ULONG, ulFlags), (LPUNKNOWN, lpCollector), (LPSPropTagArray, lpIncludeProps), (LPSPropTagArray, lpExcludeProps), (ULONG, ulBufferSize))
  570. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, Synchronize, (ULONG *, pulSteps), (ULONG *, pulProgress))
  571. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, UpdateState, (LPSTREAM, lpStream))
  572. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, GetChangeCount, (ULONG *, lpcChanges))
  573. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, SetMessageInterface, (REFIID, refiid))
  574. DEF_HRMETHOD1(TRACE_MAPI, ECExchangeExportChanges, ECExportChanges, SetLogger, (ECLogger *, lpLogger))
  575. HRESULT ECExchangeExportChanges::ExportMessageChanges() {
  576. assert(m_lpImportContents != NULL);
  577. if (m_lpImportStreamedContents != NULL)
  578. return ExportMessageChangesFast();
  579. else
  580. return ExportMessageChangesSlow();
  581. }
  582. HRESULT ECExchangeExportChanges::ExportMessageChangesSlow() {
  583. HRESULT hr = hrSuccess;
  584. memory_ptr<SPropValue> lpPropArray;
  585. memory_ptr<SPropTagArray> lpPropTagArray;
  586. ULONG ulObjType;
  587. ULONG ulCount;
  588. ULONG ulFlags;
  589. ULONG ulSteps = 0;
  590. ULONG cbEntryID = 0;
  591. memory_ptr<ENTRYID> lpEntryID;
  592. static constexpr const SizedSPropTagArray(5, sptMessageExcludes) =
  593. {5, {PR_MESSAGE_SIZE, PR_MESSAGE_RECIPIENTS,
  594. PR_MESSAGE_ATTACHMENTS, PR_ATTACH_SIZE, PR_PARENT_SOURCE_KEY}};
  595. static constexpr const SizedSPropTagArray(7, sptImportProps) =
  596. {7, {PR_SOURCE_KEY, PR_LAST_MODIFICATION_TIME, PR_CHANGE_KEY,
  597. PR_PARENT_SOURCE_KEY, PR_PREDECESSOR_CHANGE_LIST, PR_ENTRYID,
  598. PR_ASSOCIATED}};
  599. static constexpr const SizedSPropTagArray(1, sptAttach) = {1, {PR_ATTACH_NUM}};
  600. while(m_ulStep < m_lstChange.size() && (m_ulBufferSize == 0 || ulSteps < m_ulBufferSize)){
  601. ulFlags = 0;
  602. if ((m_lstChange.at(m_ulStep).ulChangeType & ICS_ACTION_MASK) == ICS_NEW)
  603. ulFlags |= SYNC_NEW_MESSAGE;
  604. object_ptr<IMessage> lpSourceMessage, lpDestMessage;
  605. object_ptr<IMAPITable> lpTable;
  606. rowset_ptr lpRows;
  607. if(!m_sourcekey.empty()) {
  608. // Normal exporter, get the import properties we need by opening the source message
  609. hr = m_lpStore->EntryIDFromSourceKey(
  610. m_lstChange.at(m_ulStep).sParentSourceKey.cb,
  611. m_lstChange.at(m_ulStep).sParentSourceKey.lpb,
  612. m_lstChange.at(m_ulStep).sSourceKey.cb,
  613. m_lstChange.at(m_ulStep).sSourceKey.lpb,
  614. &cbEntryID, &~lpEntryID);
  615. if(hr == MAPI_E_NOT_FOUND){
  616. hr = hrSuccess;
  617. goto next;
  618. }
  619. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change sourcekey: %s", bin2hex(m_lstChange.at(m_ulStep).sSourceKey.cb, m_lstChange.at(m_ulStep).sSourceKey.lpb).c_str());
  620. if(hr != hrSuccess) {
  621. ZLOG_DEBUG(m_lpLogger, "Error while getting entryid from sourcekey %s", bin2hex(m_lstChange.at(m_ulStep).sSourceKey.cb, m_lstChange.at(m_ulStep).sSourceKey.lpb).c_str());
  622. goto exit;
  623. }
  624. hr = m_lpStore->OpenEntry(cbEntryID, lpEntryID, &m_iidMessage, MAPI_MODIFY, &ulObjType, &~lpSourceMessage);
  625. if(hr == MAPI_E_NOT_FOUND){
  626. hr = hrSuccess;
  627. goto next;
  628. }
  629. if(hr != hrSuccess) {
  630. ZLOG_DEBUG(m_lpLogger, "Unable to open message with entryid %s", bin2hex(cbEntryID, reinterpret_cast<const unsigned char *>(lpEntryID.get())).c_str());
  631. goto exit;
  632. }
  633. hr = lpSourceMessage->GetProps(sptImportProps, 0, &ulCount, &~lpPropArray);
  634. if(FAILED(hr)) {
  635. ZLOG_DEBUG(m_lpLogger, "Unable to get properties from source message");
  636. goto exit;
  637. }
  638. hr = m_lpImportContents->ImportMessageChange(ulCount, lpPropArray, ulFlags, &~lpDestMessage);
  639. } else {
  640. // Server-wide ICS exporter, only export source key and parent source key
  641. SPropValue sProps[2];
  642. sProps[0].ulPropTag = PR_SOURCE_KEY;
  643. sProps[0].Value.bin = m_lstChange.at(m_ulStep).sSourceKey; // cheap copy is ok since pointer is valid during ImportMessageChange call
  644. sProps[1].ulPropTag = PR_PARENT_SOURCE_KEY;
  645. sProps[1].Value.bin = m_lstChange.at(m_ulStep).sParentSourceKey;
  646. hr = m_lpImportContents->ImportMessageChange(2, sProps, ulFlags, &~lpDestMessage);
  647. }
  648. if (hr == SYNC_E_IGNORE) {
  649. m_lpLogger->Log(EC_LOGLEVEL_INFO, "ignored change");
  650. // Mark this change as processed
  651. hr = hrSuccess;
  652. goto next;
  653. }else if(hr == SYNC_E_OBJECT_DELETED){
  654. m_lpLogger->Log(EC_LOGLEVEL_INFO, "ignored change for deleted item");
  655. // Mark this change as processed
  656. hr = hrSuccess;
  657. goto next;
  658. }else if(hr == SYNC_E_INVALID_PARAMETER){
  659. //exchange doesn't like our input sometimes
  660. m_lpLogger->Log(EC_LOGLEVEL_INFO, "ignored change parameter");
  661. hr = hrSuccess;
  662. goto next;
  663. // TODO handle SYNC_E_OBJECT_DELETED, SYNC_E_CONFLICT, SYNC_E_NO_PARENT, SYNC_E_INCEST, SYNC_E_UNSYNCHRONIZED
  664. }else if(hr != hrSuccess){
  665. //m_lpLogger->Log(EC_LOGLEVEL_INFO, "change error: %s", stringify(hr, true).c_str());
  666. ZLOG_DEBUG(m_lpLogger, "Error during message import");
  667. goto exit;
  668. }
  669. if (lpDestMessage == NULL)
  670. // Import succeeded, but we have no message to export to. Treat this the same as
  671. // SYNC_E_IGNORE. This is required for the BES ICS exporter
  672. goto next;
  673. if (lpSourceMessage == nullptr)
  674. /*
  675. * Well this is getting silly. If we do not have a
  676. * source message, where the heck should we copy from?
  677. */
  678. goto next;
  679. hr = lpSourceMessage->CopyTo(0, NULL, sptMessageExcludes, 0,
  680. NULL, &IID_IMessage, lpDestMessage, 0, NULL);
  681. if(hr != hrSuccess) {
  682. ZLOG_DEBUG(m_lpLogger, "Unable to copy to imported message");
  683. goto exit;
  684. }
  685. hr = lpSourceMessage->GetRecipientTable(0, &~lpTable);
  686. if(hr != hrSuccess) {
  687. ZLOG_DEBUG(m_lpLogger, "Unable to read source message's recipient table");
  688. goto exit;
  689. }
  690. hr = lpTable->QueryColumns(TBL_ALL_COLUMNS, &~lpPropTagArray);
  691. if (hr != hrSuccess) {
  692. ZLOG_DEBUG(m_lpLogger, "Unable to get column set from source message's recipient table");
  693. goto exit;
  694. }
  695. hr = lpTable->SetColumns(lpPropTagArray, 0);
  696. if (hr != hrSuccess) {
  697. ZLOG_DEBUG(m_lpLogger, "Unable to set column set for source message's recipient table");
  698. goto exit;
  699. }
  700. hr = lpTable->QueryRows(0xFFFF, 0, &~lpRows);
  701. if(hr != hrSuccess) {
  702. ZLOG_DEBUG(m_lpLogger, "Unable to read recipients from source message");
  703. goto exit;
  704. }
  705. //FIXME: named property in the recipienttable ?
  706. hr = lpDestMessage->ModifyRecipients(0, reinterpret_cast<ADRLIST *>(lpRows.get()));
  707. if(hr != hrSuccess)
  708. hr = hrSuccess;
  709. //goto exit;
  710. //delete every attachment
  711. hr = lpDestMessage->GetAttachmentTable(0, &~lpTable);
  712. if(hr != hrSuccess) {
  713. ZLOG_DEBUG(m_lpLogger, "Unable to get destination's attachment table");
  714. goto exit;
  715. }
  716. hr = lpTable->SetColumns(sptAttach, 0);
  717. if(hr != hrSuccess) {
  718. ZLOG_DEBUG(m_lpLogger, "Unable to set destination's attachment table's column set");
  719. goto exit;
  720. }
  721. hr = lpTable->QueryRows(0xFFFF, 0, &~lpRows);
  722. if(hr != hrSuccess) {
  723. ZLOG_DEBUG(m_lpLogger, "Unable to read destination's attachment list");
  724. goto exit;
  725. }
  726. for (ulCount = 0; ulCount < lpRows->cRows; ++ulCount) {
  727. hr = lpDestMessage->DeleteAttach(lpRows->aRow[ulCount].lpProps[0].Value.ul, 0, NULL, 0);
  728. if(hr != hrSuccess) {
  729. ZLOG_DEBUG(m_lpLogger, "Unable to delete destination's attachment number %d", lpRows->aRow[ulCount].lpProps[0].Value.ul);
  730. goto exit;
  731. }
  732. }
  733. //add every attachment
  734. hr = lpSourceMessage->GetAttachmentTable(0, &~lpTable);
  735. if(hr != hrSuccess)
  736. goto exit;
  737. hr = lpTable->SetColumns(sptAttach, 0);
  738. if(hr != hrSuccess)
  739. goto exit;
  740. hr = lpTable->QueryRows(0xFFFF, 0, &~lpRows);
  741. if(hr != hrSuccess)
  742. goto exit;
  743. for (ulCount = 0; ulCount < lpRows->cRows; ++ulCount) {
  744. object_ptr<IAttach> lpSourceAttach, lpDestAttach;
  745. hr = lpSourceMessage->OpenAttach(lpRows->aRow[ulCount].lpProps[0].Value.ul, &IID_IAttachment, 0, &~lpSourceAttach);
  746. if(hr != hrSuccess) {
  747. ZLOG_DEBUG(m_lpLogger, "Unable to open attachment %d in source message", lpRows->aRow[ulCount].lpProps[0].Value.ul);
  748. goto exit;
  749. }
  750. hr = lpDestMessage->CreateAttach(&IID_IAttachment, 0, &ulObjType, &~lpDestAttach);
  751. if(hr != hrSuccess) {
  752. ZLOG_DEBUG(m_lpLogger, "Unable to create attachment");
  753. goto exit;
  754. }
  755. hr = lpSourceAttach->CopyTo(0, NULL, sptAttach, 0,
  756. NULL, &IID_IAttachment, lpDestAttach, 0, NULL);
  757. if(hr != hrSuccess) {
  758. ZLOG_DEBUG(m_lpLogger, "Unable to copy attachment");
  759. goto exit;
  760. }
  761. hr = lpDestAttach->SaveChanges(0);
  762. if(hr != hrSuccess) {
  763. ZLOG_DEBUG(m_lpLogger, "SaveChanges() failed for destination attachment");
  764. goto exit;
  765. }
  766. }
  767. lpTable.release();
  768. hr = lpSourceMessage->GetPropList(0, &~lpPropTagArray);
  769. if (hr != hrSuccess) {
  770. ZLOG_DEBUG(m_lpLogger, "Unable to get property list of source message");
  771. goto exit;
  772. }
  773. hr = Util::HrDeleteResidualProps(lpDestMessage, lpSourceMessage, lpPropTagArray);
  774. if (hr != hrSuccess) {
  775. ZLOG_DEBUG(m_lpLogger, "Unable to remove old properties from destination message");
  776. goto exit;
  777. }
  778. hr = lpDestMessage->SaveChanges(0);
  779. if(hr != hrSuccess) {
  780. ZLOG_DEBUG(m_lpLogger, "SaveChanges failed for destination message");
  781. goto exit;
  782. }
  783. next:
  784. // Mark this change as processed, even if we skipped it due to SYNC_E_IGNORE or because the item was deleted on the source server
  785. m_setProcessedChanges.insert(std::pair<unsigned int, std::string>(m_lstChange.at(m_ulStep).ulChangeId, std::string((char *)m_lstChange.at(m_ulStep).sSourceKey.lpb, m_lstChange.at(m_ulStep).sSourceKey.cb)));
  786. ++m_ulStep;
  787. ++ulSteps;
  788. }
  789. if(m_ulStep < m_lstChange.size())
  790. hr = SYNC_W_PROGRESS;
  791. exit:
  792. if(hr != hrSuccess && hr != SYNC_W_PROGRESS)
  793. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change error: %s", stringify(hr, true).c_str());
  794. return hr;
  795. }
  796. HRESULT ECExchangeExportChanges::ExportMessageChangesFast()
  797. {
  798. HRESULT hr = hrSuccess;
  799. WSSerializedMessagePtr ptrSerializedMessage;
  800. ULONG cbProps = 0;
  801. SPropValuePtr ptrProps;
  802. const SPropValue *lpPropVal = NULL;
  803. ULONG ulFlags = 0;
  804. StreamPtr ptrDestStream;
  805. static constexpr const SizedSPropTagArray(11, sptImportProps) = { 11, {
  806. PR_SOURCE_KEY,
  807. PR_LAST_MODIFICATION_TIME,
  808. PR_CHANGE_KEY,
  809. PR_PARENT_SOURCE_KEY,
  810. PR_PREDECESSOR_CHANGE_LIST,
  811. PR_ENTRYID,
  812. PR_ASSOCIATED,
  813. PR_MESSAGE_FLAGS, /* needed for backward compat since PR_ASSOCIATED is not supported on earlier systems */
  814. PR_STORE_RECORD_KEY,
  815. PR_EC_HIERARCHYID,
  816. PR_EC_PARENT_HIERARCHYID
  817. } };
  818. static constexpr const SizedSPropTagArray(7, sptImportPropsServerWide) = { 7, {
  819. PR_SOURCE_KEY,
  820. PR_PARENT_SOURCE_KEY,
  821. PR_STORE_RECORD_KEY,
  822. PR_STORE_ENTRYID,
  823. PR_EC_HIERARCHYID,
  824. PR_EC_PARENT_HIERARCHYID,
  825. PR_ENTRYID
  826. } };
  827. const auto lpImportProps = m_sourcekey.empty() ? sptImportPropsServerWide : sptImportProps;
  828. // No more changes (add/modify).
  829. ZLOG_DEBUG(m_lpLogger, "ExportFast: At step %u, changeset contains %lu items)",
  830. m_ulStep, static_cast<unsigned long>(m_lstChange.size()));
  831. if (m_ulStep >= m_lstChange.size())
  832. goto exit;
  833. if (!m_ptrStreamExporter || m_ptrStreamExporter->IsDone()) {
  834. ZLOG_DEBUG(m_lpLogger, "ExportFast: Requesting new batch, batch size = %u", m_ulBatchSize);
  835. hr = m_lpStore->ExportMessageChangesAsStream(m_ulFlags & (SYNC_BEST_BODY | SYNC_LIMITED_IMESSAGE), m_ulEntryPropTag, m_lstChange, m_ulStep, m_ulBatchSize, lpImportProps, &~m_ptrStreamExporter);
  836. if (hr == MAPI_E_UNABLE_TO_COMPLETE) {
  837. // There was nothing to export (see ExportMessageChangesAsStream documentation)
  838. assert(m_ulStep >= m_lstChange.size()); // @todo: Is this a correct assumption?
  839. hr = hrSuccess;
  840. goto exit;
  841. } else if (hr != hrSuccess) {
  842. ZLOG_DEBUG(m_lpLogger, "ExportFast: %s", "Stream export failed");
  843. goto exit;
  844. }
  845. ZLOG_DEBUG(m_lpLogger, "ExportFast: %s", "Got new batch");
  846. }
  847. ZLOG_DEBUG(m_lpLogger, "ExportFast: Requesting serialized message, step = %u", m_ulStep);
  848. hr = m_ptrStreamExporter->GetSerializedMessage(m_ulStep, &~ptrSerializedMessage);
  849. if (hr == SYNC_E_OBJECT_DELETED) {
  850. ZLOG_DEBUG(m_lpLogger, "ExportFast: %s", "Source message is deleted");
  851. hr = hrSuccess;
  852. goto skip;
  853. } else if (hr != hrSuccess) {
  854. ZLOG_DEBUG(m_lpLogger, "ExportFast: Unable to get serialized message, hr = 0x%08x", hr);
  855. goto exit;
  856. }
  857. hr = ptrSerializedMessage->GetProps(&cbProps, &~ptrProps);
  858. if (hr != hrSuccess) {
  859. ZLOG_DEBUG(m_lpLogger, "ExportFast: %s", "Unable to get required properties from serialized message");
  860. goto exit;
  861. }
  862. lpPropVal = PCpropFindProp(ptrProps, cbProps, PR_MESSAGE_FLAGS);
  863. if (lpPropVal != NULL && (lpPropVal->Value.ul & MSGFLAG_ASSOCIATED))
  864. ulFlags |= SYNC_ASSOCIATED;
  865. if ((m_lstChange.at(m_ulStep).ulChangeType & ICS_ACTION_MASK) == ICS_NEW)
  866. ulFlags |= SYNC_NEW_MESSAGE;
  867. ZLOG_DEBUG(m_lpLogger, "ExportFast: %s", "Importing message change");
  868. hr = m_lpImportStreamedContents->ImportMessageChangeAsAStream(cbProps, ptrProps, ulFlags, &~ptrDestStream);
  869. if (hr == hrSuccess) {
  870. ZLOG_DEBUG(m_lpLogger, "ExportFast: %s", "Copying data");
  871. hr = ptrSerializedMessage->CopyData(ptrDestStream);
  872. if (hr != hrSuccess) {
  873. ZLOG_DEBUG(m_lpLogger, "ExportFast: Failed to copy data, hr = 0x%08x", hr);
  874. LogMessageProps(EC_LOGLEVEL_DEBUG, cbProps, ptrProps);
  875. goto exit;
  876. }
  877. ZLOG_DEBUG(m_lpLogger, "ExportFast: %s", "Copied data");
  878. } else if (hr == SYNC_E_IGNORE || hr == SYNC_E_OBJECT_DELETED) {
  879. ZLOG_DEBUG(m_lpLogger, "ExportFast: Change ignored, code = 0x%08x", hr);
  880. hr = ptrSerializedMessage->DiscardData();
  881. if (hr != hrSuccess) {
  882. ZLOG_DEBUG(m_lpLogger, "ExportFast: Failed to discard data, hr = 0x%08x", hr);
  883. LogMessageProps(EC_LOGLEVEL_DEBUG, cbProps, ptrProps);
  884. goto exit;
  885. }
  886. } else {
  887. ZLOG_DEBUG(m_lpLogger, "ExportFast: Import failed, hr = 0x%08x", hr);
  888. LogMessageProps(EC_LOGLEVEL_DEBUG, cbProps, ptrProps);
  889. goto exit;
  890. }
  891. skip:
  892. m_setProcessedChanges.insert(std::pair<unsigned int, std::string>(m_lstChange.at(m_ulStep).ulChangeId, std::string((char *)m_lstChange.at(m_ulStep).sSourceKey.lpb, m_lstChange.at(m_ulStep).sSourceKey.cb)));
  893. if (++m_ulStep < m_lstChange.size())
  894. hr = SYNC_W_PROGRESS;
  895. exit:
  896. if (FAILED(hr))
  897. m_ptrStreamExporter.reset();
  898. ZLOG_DEBUG(m_lpLogger, "ExportFast: Done, hr = 0x%08x", hr);
  899. return hr;
  900. }
  901. HRESULT ECExchangeExportChanges::ExportMessageFlags(){
  902. HRESULT hr = hrSuccess;
  903. memory_ptr<READSTATE> lpReadState;
  904. ULONG ulCount;
  905. if(m_lstFlag.empty())
  906. goto exit;
  907. hr = MAPIAllocateBuffer(sizeof(READSTATE) * m_lstFlag.size(), &~lpReadState);
  908. if (hr != hrSuccess)
  909. goto exit;
  910. ulCount = 0;
  911. for (const auto &change : m_lstFlag) {
  912. hr = MAPIAllocateMore(change.sSourceKey.cb, lpReadState, reinterpret_cast<LPVOID *>(&lpReadState[ulCount].pbSourceKey));
  913. if (hr != hrSuccess)
  914. goto exit;
  915. lpReadState[ulCount].cbSourceKey = change.sSourceKey.cb;
  916. memcpy(lpReadState[ulCount].pbSourceKey, change.sSourceKey.lpb, change.sSourceKey.cb);
  917. lpReadState[ulCount].ulFlags = change.ulFlags;
  918. ++ulCount;
  919. }
  920. if(ulCount > 0){
  921. hr = m_lpImportContents->ImportPerUserReadStateChange(ulCount, lpReadState);
  922. if (hr == SYNC_E_IGNORE)
  923. hr = hrSuccess;
  924. if(hr != hrSuccess) {
  925. ZLOG_DEBUG(m_lpLogger, "Read state change failed");
  926. goto exit;
  927. }
  928. // Mark the flag changes as processed
  929. for (const auto &change : m_lstFlag)
  930. m_setProcessedChanges.insert(std::pair<unsigned int, std::string>(change.ulChangeId, std::string(reinterpret_cast<const char *>(change.sSourceKey.lpb), change.sSourceKey.cb)));
  931. }
  932. exit:
  933. if (hr != hrSuccess)
  934. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to sync message flags, 0x%08X", hr);
  935. return hr;
  936. }
  937. HRESULT ECExchangeExportChanges::ExportMessageDeletes(){
  938. HRESULT hr = hrSuccess;
  939. memory_ptr<ENTRYLIST> lpEntryList;
  940. if(!m_lstSoftDelete.empty()){
  941. hr = ChangesToEntrylist(&m_lstSoftDelete, &~lpEntryList);
  942. if(hr != hrSuccess)
  943. return hr;
  944. hr = m_lpImportContents->ImportMessageDeletion(SYNC_SOFT_DELETE, lpEntryList);
  945. if (hr == SYNC_E_IGNORE)
  946. hr = hrSuccess;
  947. if(hr != hrSuccess) {
  948. ZLOG_DEBUG(m_lpLogger, "Message deletion import failed");
  949. return hr;
  950. }
  951. hr = AddProcessedChanges(m_lstSoftDelete);
  952. if (hr != hrSuccess) {
  953. ZLOG_DEBUG(m_lpLogger, "Unable to add processed soft deletion changes");
  954. return hr;
  955. }
  956. }
  957. if(!m_lstHardDelete.empty()){
  958. hr = ChangesToEntrylist(&m_lstHardDelete, &~lpEntryList);
  959. if(hr != hrSuccess) {
  960. ZLOG_DEBUG(m_lpLogger, "Unable to create entry list");
  961. return hr;
  962. }
  963. hr = m_lpImportContents->ImportMessageDeletion(0, lpEntryList);
  964. if (hr == SYNC_E_IGNORE)
  965. hr = hrSuccess;
  966. if(hr != hrSuccess) {
  967. ZLOG_DEBUG(m_lpLogger, "Message hard deletion failed");
  968. return hr;
  969. }
  970. hr = AddProcessedChanges(m_lstHardDelete);
  971. if (hr != hrSuccess) {
  972. ZLOG_DEBUG(m_lpLogger, "Unable to add processed hard deletion changes");
  973. return hr;
  974. }
  975. }
  976. return hrSuccess;
  977. }
  978. HRESULT ECExchangeExportChanges::ExportFolderChanges(){
  979. HRESULT hr = hrSuccess;
  980. LPSPropValue lpPropVal = NULL;
  981. ULONG ulObjType = 0;
  982. ULONG ulCount = 0;
  983. ULONG ulSteps = 0;
  984. ULONG cbEntryID = 0;
  985. while(m_ulStep < m_lstChange.size() && (m_ulBufferSize == 0 || ulSteps < m_ulBufferSize)){
  986. object_ptr<IMAPIFolder> lpFolder;
  987. memory_ptr<SPropValue> lpPropArray;
  988. memory_ptr<ENTRYID> lpEntryID;
  989. if(!m_sourcekey.empty()) {
  990. // Normal export, need all properties
  991. hr = m_lpStore->EntryIDFromSourceKey(
  992. m_lstChange.at(m_ulStep).sSourceKey.cb,
  993. m_lstChange.at(m_ulStep).sSourceKey.lpb,
  994. 0, NULL,
  995. &cbEntryID, &~lpEntryID);
  996. if(hr != hrSuccess){
  997. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change sourcekey not found");
  998. hr = hrSuccess;
  999. goto next;
  1000. }
  1001. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change sourcekey: %s", bin2hex(m_lstChange.at(m_ulStep).sSourceKey.cb, m_lstChange.at(m_ulStep).sSourceKey.lpb).c_str());
  1002. hr = m_lpStore->OpenEntry(cbEntryID, lpEntryID, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpFolder);
  1003. if(hr != hrSuccess){
  1004. hr = hrSuccess;
  1005. goto next;
  1006. }
  1007. hr = HrGetAllProps(lpFolder, (m_ulFlags & SYNC_UNICODE ? MAPI_UNICODE : 0), &ulCount, &~lpPropArray);
  1008. if(FAILED(hr)) {
  1009. ZLOG_DEBUG(m_lpLogger, "Unable to get source folder properties");
  1010. return hr;
  1011. }
  1012. //for folders directly under m_lpFolder PR_PARENT_SOURCE_KEY must be NULL
  1013. //this protects against recursive problems during syncing when the PR_PARENT_SOURCE_KEY
  1014. //equals the sourcekey of the folder which we are already syncing.
  1015. //If the exporter sends an empty PR_PARENT_SOURCE_KEY to the importer the importer can
  1016. //assume the parent is the folder which it is syncing.
  1017. lpPropVal = PpropFindProp(lpPropArray, ulCount, PR_PARENT_SOURCE_KEY);
  1018. if (lpPropVal != nullptr && m_sourcekey.size() == lpPropVal->Value.bin.cb &&
  1019. memcmp(lpPropVal->Value.bin.lpb, m_sourcekey.c_str(), m_sourcekey.size()) == 0)
  1020. lpPropVal->Value.bin.cb = 0;
  1021. hr = m_lpImportHierarchy->ImportFolderChange(ulCount, lpPropArray);
  1022. } else {
  1023. // Server-wide ICS
  1024. SPropValue sProps[1];
  1025. sProps[0].ulPropTag = PR_SOURCE_KEY;
  1026. sProps[0].Value.bin = m_lstChange.at(m_ulStep).sSourceKey;
  1027. hr = m_lpImportHierarchy->ImportFolderChange(1, sProps);
  1028. }
  1029. if (hr == SYNC_E_IGNORE){
  1030. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change ignored");
  1031. hr = hrSuccess;
  1032. goto next;
  1033. }else if (hr == MAPI_E_INVALID_PARAMETER){
  1034. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change invalid parameter");
  1035. hr = hrSuccess;
  1036. goto next;
  1037. }else if(hr == MAPI_E_NOT_FOUND){
  1038. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change not found");
  1039. hr = hrSuccess;
  1040. goto next;
  1041. }else if(FAILED(hr)) {
  1042. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change error: %s", stringify(hr, true).c_str());
  1043. return hr;
  1044. }else if(hr != hrSuccess){
  1045. m_lpLogger->Log(EC_LOGLEVEL_WARNING, "change warning: %s", stringify(hr, true).c_str());
  1046. }
  1047. next:
  1048. // Mark this change as processed
  1049. m_setProcessedChanges.insert(std::pair<unsigned int, std::string>(m_lstChange.at(m_ulStep).ulChangeId, std::string((char *)m_lstChange.at(m_ulStep).sSourceKey.lpb, m_lstChange.at(m_ulStep).sSourceKey.cb)));
  1050. ++ulSteps;
  1051. ++m_ulStep;
  1052. }
  1053. if(m_ulStep < m_lstChange.size())
  1054. hr = SYNC_W_PROGRESS;
  1055. return hr;
  1056. }
  1057. HRESULT ECExchangeExportChanges::ExportFolderDeletes(){
  1058. HRESULT hr = hrSuccess;
  1059. memory_ptr<ENTRYLIST> lpEntryList;
  1060. if(!m_lstSoftDelete.empty()){
  1061. hr = ChangesToEntrylist(&m_lstSoftDelete, &~lpEntryList);
  1062. if(hr != hrSuccess) {
  1063. ZLOG_DEBUG(m_lpLogger, "Unable to create folder deletion entry list");
  1064. return hr;
  1065. }
  1066. hr = m_lpImportHierarchy->ImportFolderDeletion(SYNC_SOFT_DELETE, lpEntryList);
  1067. if (hr == SYNC_E_IGNORE)
  1068. hr = hrSuccess;
  1069. if(hr != hrSuccess) {
  1070. ZLOG_DEBUG(m_lpLogger, "Unable to import folder deletions");
  1071. return hr;
  1072. }
  1073. hr = AddProcessedChanges(m_lstSoftDelete);
  1074. if (hr != hrSuccess) {
  1075. ZLOG_DEBUG(m_lpLogger, "Unable to add processed folder soft deletions");
  1076. return hr;
  1077. }
  1078. }
  1079. if(!m_lstHardDelete.empty()){
  1080. hr = ChangesToEntrylist(&m_lstHardDelete, &~lpEntryList);
  1081. if(hr != hrSuccess) {
  1082. ZLOG_DEBUG(m_lpLogger, "Unable to create folder hard delete entry list");
  1083. return hr;
  1084. }
  1085. hr = m_lpImportHierarchy->ImportFolderDeletion(0, lpEntryList);
  1086. if (hr == SYNC_E_IGNORE)
  1087. hr = hrSuccess;
  1088. if(hr != hrSuccess) {
  1089. ZLOG_DEBUG(m_lpLogger, "Hard delete folder import failed");
  1090. return hr;
  1091. }
  1092. hr = AddProcessedChanges(m_lstHardDelete);
  1093. if (hr != hrSuccess) {
  1094. ZLOG_DEBUG(m_lpLogger, "Unable to add processed folder hard deletions");
  1095. return hr;
  1096. }
  1097. }
  1098. return hrSuccess;
  1099. }
  1100. //write in the stream 4 bytes syncid, 4 bytes changeid, 4 bytes changecount, {4 bytes changeid, 4 bytes sourcekeysize, sourcekey}
  1101. HRESULT ECExchangeExportChanges::UpdateStream(LPSTREAM lpStream){
  1102. HRESULT hr = hrSuccess;
  1103. LARGE_INTEGER liPos = {{0, 0}};
  1104. ULARGE_INTEGER liZero = {{0, 0}};
  1105. ULONG ulSize;
  1106. ULONG ulChangeCount = 0;
  1107. ULONG ulChangeId = 0;
  1108. ULONG ulSourceKeySize = 0;
  1109. if(lpStream == NULL)
  1110. goto exit;
  1111. hr = lpStream->SetSize(liZero);
  1112. if(hr != hrSuccess)
  1113. goto exit;
  1114. hr = lpStream->Seek(liPos, STREAM_SEEK_SET, NULL);
  1115. if(hr != hrSuccess)
  1116. goto exit;
  1117. hr = lpStream->Write(&m_ulSyncId, 4, &ulSize);
  1118. if(hr != hrSuccess)
  1119. goto exit;
  1120. if (m_ulSyncId == 0)
  1121. m_ulChangeId = 0;
  1122. hr = lpStream->Write(&m_ulChangeId, 4, &ulSize);
  1123. if(hr != hrSuccess)
  1124. goto exit;
  1125. if(!m_setProcessedChanges.empty()) {
  1126. ulChangeCount = m_setProcessedChanges.size();
  1127. hr = lpStream->Write(&ulChangeCount, 4, &ulSize);
  1128. if(hr != hrSuccess)
  1129. goto exit;
  1130. for (const auto &pc : m_setProcessedChanges) {
  1131. ulChangeId = pc.first;
  1132. hr = lpStream->Write(&ulChangeId, 4, &ulSize);
  1133. if(hr != hrSuccess)
  1134. goto exit;
  1135. ulSourceKeySize = pc.second.size();
  1136. hr = lpStream->Write(&ulSourceKeySize, 4, &ulSize);
  1137. if(hr != hrSuccess)
  1138. goto exit;
  1139. hr = lpStream->Write(pc.second.c_str(), pc.second.size(), &ulSize);
  1140. if(hr != hrSuccess)
  1141. goto exit;
  1142. }
  1143. }
  1144. // Seek back to the beginning after we've finished
  1145. lpStream->Seek(liPos, STREAM_SEEK_SET, NULL);
  1146. exit:
  1147. if(hr != hrSuccess)
  1148. ZLOG_DEBUG(m_lpLogger, "Stream operation failed");
  1149. return hr;
  1150. }
  1151. //convert (delete) changes to entrylist for message and folder deletion.
  1152. HRESULT ECExchangeExportChanges::ChangesToEntrylist(std::list<ICSCHANGE> * lpLstChanges, LPENTRYLIST * lppEntryList){
  1153. HRESULT hr = hrSuccess;
  1154. memory_ptr<ENTRYLIST> lpEntryList;
  1155. ULONG ulCount = 0;
  1156. hr = MAPIAllocateBuffer(sizeof(ENTRYLIST), &~lpEntryList);
  1157. if (hr != hrSuccess)
  1158. return hr;
  1159. lpEntryList->cValues = lpLstChanges->size();
  1160. if(lpEntryList->cValues > 0){
  1161. if ((hr = MAPIAllocateMore(sizeof(SBinary) * lpEntryList->cValues, lpEntryList, (LPVOID *)&lpEntryList->lpbin)) != hrSuccess)
  1162. return hr;
  1163. ulCount = 0;
  1164. for (const auto &change : *lpLstChanges) {
  1165. lpEntryList->lpbin[ulCount].cb = change.sSourceKey.cb;
  1166. hr = MAPIAllocateMore(change.sSourceKey.cb, lpEntryList,
  1167. reinterpret_cast<void **>(&lpEntryList->lpbin[ulCount].lpb));
  1168. if (hr != hrSuccess)
  1169. return hr;
  1170. memcpy(lpEntryList->lpbin[ulCount].lpb, change.sSourceKey.lpb, change.sSourceKey.cb);
  1171. ++ulCount;
  1172. }
  1173. }else{
  1174. lpEntryList->lpbin = NULL;
  1175. }
  1176. lpEntryList->cValues = ulCount;
  1177. *lppEntryList = lpEntryList.release();
  1178. return hrSuccess;
  1179. }
  1180. /**
  1181. * Add processed changes to the precessed changes list
  1182. *
  1183. * @param[in] lstChanges List with changes
  1184. *
  1185. */
  1186. HRESULT ECExchangeExportChanges::AddProcessedChanges(ChangeList &lstChanges)
  1187. {
  1188. for (const auto &i : lstChanges)
  1189. m_setProcessedChanges.insert(std::pair<unsigned int, std::string>(i.ulChangeId,
  1190. std::string(reinterpret_cast<const char *>(i.sSourceKey.lpb), i.sSourceKey.cb)));
  1191. return hrSuccess;
  1192. }
  1193. void ECExchangeExportChanges::LogMessageProps(int loglevel, ULONG cValues, LPSPropValue lpPropArray)
  1194. {
  1195. if (!m_lpLogger->Log(loglevel))
  1196. return;
  1197. auto lpPropEntryID = PCpropFindProp(lpPropArray, cValues, PR_ENTRYID);
  1198. auto lpPropSK = PCpropFindProp(lpPropArray, cValues, PR_SOURCE_KEY);
  1199. auto lpPropFlags = PCpropFindProp(lpPropArray, cValues, PR_MESSAGE_FLAGS);
  1200. auto lpPropHierarchyId = PCpropFindProp(lpPropArray, cValues, PR_EC_HIERARCHYID);
  1201. auto lpPropParentId = PCpropFindProp(lpPropArray, cValues, PR_EC_PARENT_HIERARCHYID);
  1202. m_lpLogger->Log(loglevel, "ExportFast: Message info: id=%u, parentid=%u, msgflags=%x, entryid=%s, sourcekey=%s",
  1203. lpPropHierarchyId != NULL ? lpPropHierarchyId->Value.ul : 0,
  1204. lpPropParentId != NULL ? lpPropParentId->Value.ul : 0,
  1205. lpPropFlags != NULL ? lpPropFlags->Value.ul : 0,
  1206. lpPropEntryID != NULL ? bin2hex(lpPropEntryID->Value.bin.cb, lpPropEntryID->Value.bin.lpb).c_str() : "<Unknown>",
  1207. lpPropSK != NULL ? bin2hex(lpPropSK->Value.bin.cb, lpPropSK->Value.bin.lpb).c_str() : "<Unknown>");
  1208. }