ECSyncContext.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  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 <kopano/zcdefs.h>
  18. #include <memory>
  19. #include <mutex>
  20. #include <utility>
  21. #include <kopano/platform.h>
  22. #include <kopano/lockhelper.hpp>
  23. #include <kopano/memory.hpp>
  24. #include "ECSyncContext.h"
  25. #include "ECSyncUtil.h"
  26. #include "ECSyncSettings.h"
  27. #include <IECExportAddressbookChanges.h>
  28. #include <IECExportChanges.h>
  29. #include <IECChangeAdvisor.h>
  30. #include <kopano/ECUnknown.h>
  31. #include <kopano/ECGuid.h>
  32. #include <kopano/ECTags.h>
  33. #include <kopano/ECLogger.h>
  34. #include <kopano/stringutil.h>
  35. #include <mapix.h>
  36. #include <kopano/mapiext.h>
  37. #include <mapiutil.h>
  38. #include <edkguid.h>
  39. #include <edkmdb.h>
  40. #include <kopano/mapi_ptr.h>
  41. using namespace KCHL;
  42. typedef object_ptr<IECChangeAdvisor, IID_IECChangeAdvisor> ECChangeAdvisorPtr;
  43. //DEFINEMAPIPTR(ECChangeAdvisor);
  44. typedef object_ptr<IECChangeAdviseSink, IID_IECChangeAdviseSink> ECChangeAdviseSinkPtr;
  45. //DEFINEMAPIPTR(ECChangeAdviseSink);
  46. #define EC_SYNC_STATUS_VERSION 1
  47. #define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
  48. class ECChangeAdviseSink _kc_final : public ECUnknown {
  49. public:
  50. typedef ULONG(ECSyncContext::*NOTIFYCALLBACK)(ULONG,LPENTRYLIST);
  51. ECChangeAdviseSink(ECSyncContext *lpsSyncContext, NOTIFYCALLBACK fnCallback)
  52. : m_lpsSyncContext(lpsSyncContext)
  53. , m_fnCallback(fnCallback)
  54. { }
  55. // IUnknown
  56. HRESULT QueryInterface(REFIID refiid, void **lpvoid) _kc_override
  57. {
  58. if (refiid == IID_ECUnknown || refiid == IID_ECChangeAdviseSink) {
  59. AddRef();
  60. *lpvoid = (void *)this;
  61. return hrSuccess;
  62. }
  63. if (refiid == IID_IUnknown || refiid == IID_IECChangeAdviseSink) {
  64. AddRef();
  65. *lpvoid = (void *)&this->m_xECChangeAdviseSink;
  66. return hrSuccess;
  67. }
  68. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  69. }
  70. // IExchangeChangeAdviseSink
  71. ULONG OnNotify(ULONG ulFlags, LPENTRYLIST lpEntryList) {
  72. return CALL_MEMBER_FN(*m_lpsSyncContext, m_fnCallback)(ulFlags, lpEntryList);
  73. }
  74. private:
  75. class xECChangeAdviseSink _kc_final : public IECChangeAdviseSink {
  76. public:
  77. // <kopano/xclsfrag/IUnknown.hpp>
  78. virtual ULONG __stdcall AddRef(void) _kc_override
  79. {
  80. METHOD_PROLOGUE_(ECChangeAdviseSink, ECChangeAdviseSink);
  81. return pThis->AddRef();
  82. }
  83. virtual ULONG __stdcall Release(void) _kc_override
  84. {
  85. METHOD_PROLOGUE_(ECChangeAdviseSink, ECChangeAdviseSink);
  86. return pThis->Release();
  87. }
  88. virtual HRESULT __stdcall QueryInterface(REFIID refiid, void **pInterface) _kc_override
  89. {
  90. METHOD_PROLOGUE_(ECChangeAdviseSink, ECChangeAdviseSink);
  91. return pThis->QueryInterface(refiid, pInterface);
  92. }
  93. // <kopano/xclsfrag/IExchangeChangeAdviseSink.hpp>
  94. virtual ULONG __stdcall OnNotify(ULONG ulFlags, LPENTRYLIST lpEntryList) _kc_override
  95. {
  96. METHOD_PROLOGUE_(ECChangeAdviseSink, ECChangeAdviseSink);
  97. return pThis->OnNotify(ulFlags, lpEntryList);
  98. }
  99. } m_xECChangeAdviseSink;
  100. ECSyncContext *m_lpsSyncContext;
  101. NOTIFYCALLBACK m_fnCallback;
  102. };
  103. static HRESULT HrCreateECChangeAdviseSink(ECSyncContext *lpsSyncContext,
  104. ECChangeAdviseSink::NOTIFYCALLBACK fnCallback,
  105. IECChangeAdviseSink **lppAdviseSink)
  106. {
  107. object_ptr<ECChangeAdviseSink> lpAdviseSink(new(std::nothrow) ECChangeAdviseSink(lpsSyncContext, fnCallback));
  108. if (lpAdviseSink == NULL)
  109. return MAPI_E_NOT_ENOUGH_MEMORY;
  110. HRESULT hr = lpAdviseSink->QueryInterface(IID_IECChangeAdviseSink,
  111. reinterpret_cast<void **>(lppAdviseSink));
  112. if (hr == hrSuccess)
  113. lpAdviseSink.release();
  114. return hr;
  115. }
  116. ECSyncContext::ECSyncContext(LPMDB lpStore, ECLogger *lpLogger)
  117. : m_lpStore(lpStore)
  118. , m_lpLogger(lpLogger)
  119. , m_lpSettings(ECSyncSettings::GetInstance())
  120. {
  121. m_lpLogger->AddRef();
  122. m_lpStore->AddRef();
  123. if (m_lpSettings->ChangeNotificationsEnabled())
  124. HrCreateECChangeAdviseSink(this, &ECSyncContext::OnChange, &m_lpChangeAdviseSink);
  125. }
  126. ECSyncContext::~ECSyncContext()
  127. {
  128. if (m_lpChangeAdvisor)
  129. m_lpChangeAdvisor->Release();
  130. if (m_lpChangeAdviseSink)
  131. m_lpChangeAdviseSink->Release();
  132. if (m_lpStore)
  133. m_lpStore->Release();
  134. m_lpLogger->Release();
  135. }
  136. HRESULT ECSyncContext::HrGetMsgStore(LPMDB *lppMsgStore)
  137. {
  138. if (lppMsgStore == NULL)
  139. return MAPI_E_INVALID_PARAMETER;
  140. if (m_lpStore == NULL)
  141. return MAPI_E_NOT_FOUND;
  142. return m_lpStore->QueryInterface(IID_IMsgStore,
  143. reinterpret_cast<void **>(lppMsgStore));
  144. }
  145. HRESULT ECSyncContext::HrGetReceiveFolder(LPMAPIFOLDER *lppInboxFolder)
  146. {
  147. HRESULT hr = hrSuccess;
  148. ULONG cbEntryID = 0;
  149. memory_ptr<ENTRYID> lpEntryID;
  150. ULONG ulObjType = 0;
  151. object_ptr<IMAPIFolder> lpInboxFolder;
  152. hr = m_lpStore->GetReceiveFolder((LPTSTR)"IPM", 0, &cbEntryID, &~lpEntryID, NULL);
  153. if (hr != hrSuccess)
  154. return hr;
  155. hr = m_lpStore->OpenEntry(cbEntryID, lpEntryID, &IID_IMAPIFolder, MAPI_MODIFY, &ulObjType, &~lpInboxFolder);
  156. if (hr != hrSuccess)
  157. return hr;
  158. return lpInboxFolder->QueryInterface(IID_IMAPIFolder, (void**)lppInboxFolder);
  159. }
  160. HRESULT ECSyncContext::HrGetChangeAdvisor(IECChangeAdvisor **lppChangeAdvisor)
  161. {
  162. std::unique_lock<std::mutex> lk(m_hMutex);
  163. if (!m_lpSettings->ChangeNotificationsEnabled())
  164. return MAPI_E_NO_SUPPORT;
  165. if (m_lpChangeAdvisor == NULL) {
  166. HRESULT hr = m_lpStore->OpenProperty(PR_EC_CHANGE_ADVISOR,
  167. &IID_IECChangeAdvisor, 0, 0,
  168. reinterpret_cast<LPUNKNOWN *>(&m_lpChangeAdvisor));
  169. if (hr != hrSuccess)
  170. return hr;
  171. }
  172. lk.unlock();
  173. return m_lpChangeAdvisor->QueryInterface(IID_IECChangeAdvisor,
  174. reinterpret_cast<void **>(lppChangeAdvisor));
  175. }
  176. HRESULT ECSyncContext::HrReleaseChangeAdvisor()
  177. {
  178. ECChangeAdvisorPtr ptrReleaseMe;
  179. // WARNING:
  180. // This must come after the declaration of ptrReleaseMe to
  181. // ensure the mutex is unlocked before the change advisor
  182. // is released.
  183. scoped_lock lock(m_hMutex);
  184. if (!m_lpSettings->ChangeNotificationsEnabled())
  185. return MAPI_E_NO_SUPPORT;
  186. if (m_lpChangeAdvisor) {
  187. // Don't release while holding the lock as that might
  188. // cause a deadlock if a notification is being delivered.
  189. ptrReleaseMe.reset(m_lpChangeAdvisor);
  190. m_lpChangeAdvisor = NULL;
  191. }
  192. m_mapNotifiedSyncIds.clear();
  193. return hrSuccess;
  194. }
  195. HRESULT ECSyncContext::HrResetChangeAdvisor()
  196. {
  197. ECChangeAdvisorPtr ptrChangeAdvisor;
  198. ECChangeAdviseSinkPtr ptrChangeAdviseSink;
  199. HRESULT hr = HrReleaseChangeAdvisor();
  200. if (hr != hrSuccess)
  201. return hr;
  202. hr = HrGetChangeAdvisor(&~ptrChangeAdvisor);
  203. if (hr != hrSuccess)
  204. return hr;
  205. hr = HrGetChangeAdviseSink(&~ptrChangeAdviseSink);
  206. if (hr != hrSuccess)
  207. return hr;
  208. return ptrChangeAdvisor->Config(NULL, NULL, ptrChangeAdviseSink, 0);
  209. }
  210. HRESULT ECSyncContext::HrGetChangeAdviseSink(IECChangeAdviseSink **lppChangeAdviseSink)
  211. {
  212. assert(m_lpChangeAdviseSink != NULL);
  213. return m_lpChangeAdviseSink->QueryInterface(IID_IECChangeAdviseSink, (void**)lppChangeAdviseSink);
  214. }
  215. HRESULT ECSyncContext::HrQueryHierarchyTable(LPSPropTagArray lpsPropTags, LPSRowSet *lppRows)
  216. {
  217. HRESULT hr = hrSuccess;
  218. object_ptr<IMAPIFolder> lpRootFolder;
  219. ULONG ulType = 0;
  220. object_ptr<IMAPITable> lpTable;
  221. assert(lppRows != NULL);
  222. hr = m_lpStore->OpenEntry(0, nullptr, &IID_IMAPIFolder, MAPI_DEFERRED_ERRORS, &ulType, &~lpRootFolder);
  223. if (hr != hrSuccess)
  224. return hr;
  225. hr = lpRootFolder->GetHierarchyTable(CONVENIENT_DEPTH, &~lpTable);
  226. if (hr != hrSuccess)
  227. return hr;
  228. return HrQueryAllRows(lpTable, lpsPropTags, nullptr, nullptr, 0, lppRows);
  229. }
  230. HRESULT ECSyncContext::HrOpenRootFolder(LPMAPIFOLDER *lppRootFolder, LPMDB *lppMsgStore)
  231. {
  232. HRESULT hr = hrSuccess;
  233. object_ptr<IMAPIFolder> lpRootFolder;
  234. SBinary sEntryID = {0};
  235. assert(lppRootFolder != NULL);
  236. hr = HrOpenFolder(&sEntryID, &~lpRootFolder);
  237. if (hr != hrSuccess)
  238. return hr;
  239. if (lppMsgStore) {
  240. hr = HrGetMsgStore(lppMsgStore);
  241. if (hr != hrSuccess)
  242. return hr;
  243. }
  244. *lppRootFolder = lpRootFolder.release();
  245. return hrSuccess;
  246. }
  247. HRESULT ECSyncContext::HrOpenFolder(SBinary *lpsEntryID, LPMAPIFOLDER *lppFolder)
  248. {
  249. HRESULT hr = hrSuccess;
  250. object_ptr<IMAPIFolder> lpFolder;
  251. ULONG ulType = 0;
  252. assert(lpsEntryID != NULL);
  253. assert(lppFolder != NULL);
  254. hr = m_lpStore->OpenEntry(lpsEntryID->cb, reinterpret_cast<ENTRYID *>(lpsEntryID->lpb), &IID_IMAPIFolder, MAPI_DEFERRED_ERRORS | MAPI_MODIFY, &ulType, &~lpFolder);
  255. if (hr != hrSuccess)
  256. return hr;
  257. *lppFolder = lpFolder.release();
  258. return hrSuccess;
  259. }
  260. HRESULT ECSyncContext::HrNotifyNewMail(LPNOTIFICATION lpNotification)
  261. {
  262. return m_lpStore->NotifyNewMail(lpNotification);
  263. }
  264. HRESULT ECSyncContext::HrGetSteps(SBinary *lpEntryID, SBinary *lpSourceKey, ULONG ulSyncFlags, ULONG *lpulSteps)
  265. {
  266. HRESULT hr = hrSuccess;
  267. object_ptr<IMAPIFolder> lpFolder;
  268. object_ptr<IStream> lpStream;
  269. object_ptr<IExchangeExportChanges> lpIEEC;
  270. object_ptr<IECExportChanges> lpECEC;
  271. ULONG ulChangeCount = 0;
  272. ULONG ulChangeId = 0;
  273. ULONG ulType = 0;
  274. SSyncState sSyncState = {0};
  275. object_ptr<IECChangeAdvisor> lpECA;
  276. bool bNotified = false;
  277. assert(lpulSteps != NULL);
  278. // First see if the changeadvisor is monitoring the requested folder.
  279. if (m_lpChangeAdvisor == NULL)
  280. goto fallback;
  281. hr = HrGetSyncStateFromSourceKey(lpSourceKey, &sSyncState);
  282. if (hr == MAPI_E_NOT_FOUND)
  283. goto fallback;
  284. else if (hr != hrSuccess)
  285. return hr;
  286. hr = m_lpChangeAdvisor->QueryInterface(IID_IECChangeAdvisor, &~lpECA);
  287. if (hr == MAPI_E_INTERFACE_NOT_SUPPORTED)
  288. goto fallback;
  289. else if (hr != hrSuccess)
  290. return hr;
  291. hr = lpECA->IsMonitoringSyncId(sSyncState.ulSyncId);
  292. if (hr == hrSuccess) {
  293. std::unique_lock<std::mutex> lk(m_hMutex);
  294. auto iterNotifiedSyncId = m_mapNotifiedSyncIds.find(sSyncState.ulSyncId);
  295. if (iterNotifiedSyncId == m_mapNotifiedSyncIds.cend()) {
  296. *lpulSteps = 0;
  297. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "GetSteps: sourcekey=%s, syncid=%u, notified=yes, steps=0 (unsignalled)", bin2hex(lpSourceKey->cb, lpSourceKey->lpb).c_str(), sSyncState.ulSyncId);
  298. lk.unlock();
  299. return hr;
  300. }
  301. ulChangeId = iterNotifiedSyncId->second; // Remember for later.
  302. bNotified = true;
  303. } else if (hr == MAPI_E_NOT_FOUND) {
  304. SBinary sEntry = { sizeof(sSyncState), (LPBYTE)&sSyncState };
  305. SBinaryArray sEntryList = { 1, &sEntry };
  306. hr = m_lpChangeAdvisor->AddKeys(&sEntryList);
  307. if (hr != hrSuccess)
  308. return hr;
  309. } else
  310. return hr;
  311. fallback:
  312. // The current folder is not being monitored, so get steps the old fashioned way.
  313. hr = m_lpStore->OpenEntry(lpEntryID->cb, reinterpret_cast<ENTRYID *>(lpEntryID->lpb), 0, MAPI_DEFERRED_ERRORS, &ulType, &~lpFolder);
  314. if (hr != hrSuccess)
  315. return hr;
  316. hr = HrGetSyncStatusStream(lpSourceKey, &~lpStream);
  317. if (FAILED(hr))
  318. return hr;
  319. hr = lpFolder->OpenProperty(PR_CONTENTS_SYNCHRONIZER, &IID_IExchangeExportChanges, 0, 0, &~lpIEEC);
  320. if (hr != hrSuccess)
  321. return hr;
  322. hr = lpIEEC->Config(lpStream, SYNC_CATCHUP | ulSyncFlags, NULL, NULL, NULL, NULL, 1);
  323. if (hr != hrSuccess)
  324. return hr;
  325. hr = lpIEEC->QueryInterface(IID_IECExportChanges, &~lpECEC);
  326. if (hr != hrSuccess)
  327. return hr;
  328. hr = lpECEC->GetChangeCount(&ulChangeCount);
  329. if (hr != hrSuccess)
  330. return hr;
  331. // If the change notification system was signalled for this syncid, but the server returns no results, we need
  332. // to remove it from the list. However there could have been a change in the mean time, so we need to check if
  333. // m_mapNotifiedSyncIds was updated for this syncid.
  334. if (bNotified && ulChangeCount == 0) {
  335. std::lock_guard<std::mutex> lock(m_hMutex);
  336. if (m_mapNotifiedSyncIds[sSyncState.ulSyncId] <= ulChangeId)
  337. m_mapNotifiedSyncIds.erase(sSyncState.ulSyncId);
  338. }
  339. *lpulSteps = ulChangeCount;
  340. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "GetSteps: sourcekey=%s, syncid=%u, notified=%s, steps=%u", bin2hex(lpSourceKey->cb, lpSourceKey->lpb).c_str(), sSyncState.ulSyncId, (bNotified ? "yes" : "no"), *lpulSteps);
  341. return hrSuccess;
  342. }
  343. HRESULT ECSyncContext::HrUpdateChangeId(LPSTREAM lpStream)
  344. {
  345. syncid_t ulSyncId = 0;
  346. changeid_t ulChangeId = 0;
  347. ECChangeAdvisorPtr ptrECA;
  348. assert(lpStream != NULL);
  349. HRESULT hr = HrDecodeSyncStateStream(lpStream, &ulSyncId, &ulChangeId);
  350. if (hr != hrSuccess)
  351. return hr;
  352. {
  353. std::lock_guard<std::mutex> lock(m_hMutex);
  354. if (m_mapNotifiedSyncIds[ulSyncId] <= ulChangeId)
  355. m_mapNotifiedSyncIds.erase(ulSyncId);
  356. }
  357. if(m_lpChangeAdvisor) {
  358. // Now inform the change advisor of our accomplishment
  359. hr = m_lpChangeAdvisor->QueryInterface(ptrECA.iid(), &~ptrECA);
  360. if (hr == MAPI_E_INTERFACE_NOT_SUPPORTED)
  361. return hr;
  362. hr = ptrECA->UpdateSyncState(ulSyncId, ulChangeId);
  363. if (hr == MAPI_E_INVALID_PARAMETER)
  364. // We're apparently not tracking this syncid.
  365. return hrSuccess;
  366. }
  367. return hrSuccess;
  368. }
  369. HRESULT ECSyncContext::HrGetSyncStateFromSourceKey(SBinary *lpSourceKey, SSyncState *lpsSyncState)
  370. {
  371. HRESULT hr = hrSuccess;
  372. std::string strSourceKey((char*)lpSourceKey->lpb, lpSourceKey->cb);
  373. object_ptr<IStream> lpStream;
  374. SSyncState sSyncState = {0};
  375. // First check the sourcekey to syncid map.
  376. auto iterSyncState = m_mapStates.find(strSourceKey);
  377. if (iterSyncState != m_mapStates.cend()) {
  378. assert(iterSyncState->second.ulSyncId != 0);
  379. *lpsSyncState = iterSyncState->second;
  380. return hr;
  381. }
  382. // Try to get the information from the status stream.
  383. hr = HrGetSyncStatusStream(lpSourceKey, &~lpStream);
  384. if (FAILED(hr))
  385. return hr;
  386. hr = HrDecodeSyncStateStream(lpStream, &sSyncState.ulSyncId, &sSyncState.ulChangeId, NULL);
  387. if (hr != hrSuccess)
  388. return hr;
  389. if (sSyncState.ulSyncId == 0)
  390. return MAPI_E_NOT_FOUND;
  391. // update the sourcekey to syncid map.
  392. m_mapStates.insert(SyncStateMap::value_type(strSourceKey, sSyncState));
  393. *lpsSyncState = std::move(sSyncState);
  394. return hrSuccess;
  395. }
  396. bool ECSyncContext::SyncStatusLoaded() const
  397. {
  398. return !m_mapSyncStatus.empty();
  399. }
  400. HRESULT ECSyncContext::HrClearSyncStatus()
  401. {
  402. m_mapSyncStatus.clear();
  403. return hrSuccess;
  404. }
  405. HRESULT ECSyncContext::HrLoadSyncStatus(SBinary *lpsSyncState)
  406. {
  407. ULONG ulStatusCount = 0;
  408. ULONG ulStatusNumber = 0;
  409. ULONG ulVersion = 0;
  410. ULONG ulSize = 0;
  411. ULONG ulPos = 0;
  412. std::string strSourceKey;
  413. LPSTREAM lpStream = NULL;
  414. assert(lpsSyncState != NULL);
  415. if (lpsSyncState->cb < 8)
  416. return MAPI_E_CORRUPT_DATA;
  417. HrClearSyncStatus();
  418. ulVersion = *((ULONG*)(lpsSyncState->lpb));
  419. if (ulVersion != EC_SYNC_STATUS_VERSION)
  420. return hrSuccess;
  421. ulStatusCount = *((ULONG*)(lpsSyncState->lpb+4));
  422. ZLOG_DEBUG(m_lpLogger, "Loading sync status stream: version=%u, items=%u", ulVersion, ulStatusCount);
  423. ulPos = 8;
  424. for (ulStatusNumber = 0; ulStatusNumber < ulStatusCount; ++ulStatusNumber) {
  425. ulSize = *((ULONG*)(lpsSyncState->lpb + ulPos));
  426. ulPos += 4;
  427. if (ulSize <= 16 || ulPos + ulSize + 4 > lpsSyncState->cb)
  428. return MAPI_E_CORRUPT_DATA;
  429. strSourceKey.assign((char*)(lpsSyncState->lpb + ulPos), ulSize);
  430. ulPos += ulSize;
  431. ulSize = *((ULONG*)(lpsSyncState->lpb + ulPos));
  432. ulPos += 4;
  433. if (ulSize < 8 || ulPos + ulSize > lpsSyncState->cb)
  434. return MAPI_E_CORRUPT_DATA;
  435. ZLOG_DEBUG(m_lpLogger, " Stream %u: size=%u, sourcekey=%s", ulStatusNumber, ulSize, bin2hex(strSourceKey.size(), (unsigned char*)strSourceKey.data()).c_str());
  436. HRESULT hr = CreateStreamOnHGlobal(GlobalAlloc(GPTR, ulSize), true, &lpStream);
  437. if (hr != hrSuccess)
  438. return hr;
  439. hr = lpStream->Write(lpsSyncState->lpb + ulPos, ulSize, &ulSize);
  440. if (hr != hrSuccess)
  441. return hr;
  442. m_mapSyncStatus[std::move(strSourceKey)] = lpStream;
  443. lpStream = NULL;
  444. ulPos += ulSize;
  445. }
  446. return hrSuccess;
  447. }
  448. HRESULT ECSyncContext::HrSaveSyncStatus(LPSPropValue *lppSyncStatusProp)
  449. {
  450. HRESULT hr = hrSuccess;
  451. std::string strSyncStatus;
  452. ULONG ulSize = 0;
  453. ULONG ulVersion = EC_SYNC_STATUS_VERSION;
  454. LARGE_INTEGER liPos = {{0, 0}};
  455. STATSTG sStat;
  456. memory_ptr<SPropValue> lpSyncStatusProp;
  457. assert(lppSyncStatusProp != NULL);
  458. strSyncStatus.assign((char*)&ulVersion, 4);
  459. ulSize = m_mapSyncStatus.size();
  460. strSyncStatus.append((char*)&ulSize, 4);
  461. ZLOG_DEBUG(m_lpLogger, "Saving sync status stream: items=%u", ulSize);
  462. for (const auto &ssp : m_mapSyncStatus) {
  463. std::unique_ptr<char[]> lpszStream;
  464. ulSize = ssp.first.size();
  465. strSyncStatus.append((char*)&ulSize, 4);
  466. strSyncStatus.append(ssp.first);
  467. hr = ssp.second->Stat(&sStat, STATFLAG_NONAME);
  468. if (hr != hrSuccess)
  469. return hr;
  470. ulSize = sStat.cbSize.LowPart;
  471. strSyncStatus.append((char*)&ulSize, 4);
  472. ZLOG_DEBUG(m_lpLogger, " Stream: size=%u, sourcekey=%s", ulSize,
  473. bin2hex(ssp.first.size(), reinterpret_cast<const unsigned char *>(ssp.first.data())).c_str());
  474. hr = ssp.second->Seek(liPos, STREAM_SEEK_SET, NULL);
  475. if (hr != hrSuccess)
  476. return hr;
  477. lpszStream.reset(new char[sStat.cbSize.LowPart]);
  478. hr = ssp.second->Read(lpszStream.get(), sStat.cbSize.LowPart, &ulSize);
  479. if (hr != hrSuccess)
  480. return hr;
  481. strSyncStatus.append(lpszStream.get(), sStat.cbSize.LowPart);
  482. }
  483. hr = MAPIAllocateBuffer(sizeof *lpSyncStatusProp, &~lpSyncStatusProp);
  484. if (hr != hrSuccess)
  485. return hr;
  486. memset(lpSyncStatusProp, 0, sizeof *lpSyncStatusProp);
  487. lpSyncStatusProp->Value.bin.cb = strSyncStatus.size();
  488. hr = MAPIAllocateMore(strSyncStatus.size(), lpSyncStatusProp, (void**)&lpSyncStatusProp->Value.bin.lpb);
  489. if (hr != hrSuccess)
  490. return hr;
  491. memcpy(lpSyncStatusProp->Value.bin.lpb, strSyncStatus.data(), strSyncStatus.size());
  492. *lppSyncStatusProp = lpSyncStatusProp.release();
  493. return hrSuccess;
  494. }
  495. HRESULT ECSyncContext::HrGetSyncStatusStream(LPMAPIFOLDER lpFolder, LPSTREAM *lppStream)
  496. {
  497. HRESULT hr = hrSuccess;
  498. memory_ptr<SPropValue> lpPropVal;
  499. hr = HrGetOneProp(lpFolder, PR_SOURCE_KEY, &~lpPropVal);
  500. if(hr != hrSuccess)
  501. return hr;
  502. return HrGetSyncStatusStream(&lpPropVal->Value.bin, lppStream);
  503. }
  504. HRESULT ECSyncContext::HrGetSyncStatusStream(SBinary *lpsSourceKey, LPSTREAM *lppStream)
  505. {
  506. HRESULT hr = hrSuccess;
  507. object_ptr<IStream> lpStream;
  508. std::string strSourceKey;
  509. strSourceKey.assign((char*)lpsSourceKey->lpb, lpsSourceKey->cb);
  510. auto iStatusStream = m_mapSyncStatus.find(strSourceKey);
  511. if (iStatusStream != m_mapSyncStatus.cend()) {
  512. *lppStream = iStatusStream->second;
  513. } else {
  514. hr = CreateNullStatusStream(&~lpStream);
  515. if (hr != hrSuccess)
  516. return hr;
  517. hr = MAPI_W_POSITION_CHANGED;
  518. m_mapSyncStatus[std::move(strSourceKey)] = lpStream;
  519. lpStream->AddRef();
  520. *lppStream = lpStream;
  521. }
  522. (*lppStream)->AddRef();
  523. return hrSuccess;
  524. }
  525. HRESULT ECSyncContext::GetResyncID(ULONG *lpulResyncID)
  526. {
  527. MAPIFolderPtr ptrRoot;
  528. SPropValuePtr ptrResyncID;
  529. if (lpulResyncID == NULL)
  530. return MAPI_E_INVALID_PARAMETER;
  531. HRESULT hr = HrOpenRootFolder(&~ptrRoot, nullptr);
  532. if (hr != hrSuccess)
  533. return hr;
  534. hr = HrGetOneProp(ptrRoot, PR_EC_RESYNC_ID, &~ptrResyncID);
  535. if (hr == hrSuccess)
  536. *lpulResyncID = ptrResyncID->Value.ul;
  537. else if (hr == MAPI_E_NOT_FOUND) {
  538. *lpulResyncID = 0;
  539. hr = hrSuccess;
  540. }
  541. return hr;
  542. }
  543. HRESULT ECSyncContext::SetResyncID(ULONG ulResyncID)
  544. {
  545. MAPIFolderPtr ptrRoot;
  546. SPropValue sPropResyncID;
  547. HRESULT hr = HrOpenRootFolder(&~ptrRoot, nullptr);
  548. if (hr != hrSuccess)
  549. return hr;
  550. sPropResyncID.ulPropTag = PR_EC_RESYNC_ID;
  551. sPropResyncID.Value.ul = ulResyncID;
  552. return HrSetOneProp(ptrRoot, &sPropResyncID);
  553. }
  554. HRESULT ECSyncContext::GetStoredServerUid(LPGUID lpServerUid)
  555. {
  556. MAPIFolderPtr ptrRoot;
  557. SPropValuePtr ptrServerUid;
  558. if (lpServerUid == NULL)
  559. return MAPI_E_INVALID_PARAMETER;
  560. HRESULT hr = HrOpenRootFolder(&~ptrRoot, nullptr);
  561. if (hr != hrSuccess)
  562. return hr;
  563. hr = HrGetOneProp(ptrRoot, PR_EC_STORED_SERVER_UID, &~ptrServerUid);
  564. if (hr != hrSuccess)
  565. return hr;
  566. if (ptrServerUid->Value.bin.lpb == NULL ||
  567. ptrServerUid->Value.bin.cb != sizeof(*lpServerUid))
  568. return MAPI_E_CORRUPT_DATA;
  569. memcpy(lpServerUid, ptrServerUid->Value.bin.lpb, sizeof *lpServerUid);
  570. return hrSuccess;
  571. }
  572. HRESULT ECSyncContext::SetStoredServerUid(LPGUID lpServerUid)
  573. {
  574. MAPIFolderPtr ptrRoot;
  575. SPropValue sPropServerUid;
  576. HRESULT hr = HrOpenRootFolder(&~ptrRoot, nullptr);
  577. if (hr != hrSuccess)
  578. return hr;
  579. sPropServerUid.ulPropTag = PR_EC_STORED_SERVER_UID;
  580. sPropServerUid.Value.bin.cb = sizeof *lpServerUid;
  581. sPropServerUid.Value.bin.lpb = (LPBYTE)lpServerUid;
  582. return HrSetOneProp(ptrRoot, &sPropServerUid);
  583. }
  584. HRESULT ECSyncContext::GetServerUid(LPGUID lpServerUid)
  585. {
  586. MsgStorePtr ptrStore;
  587. SPropValuePtr ptrServerUid;
  588. if (lpServerUid == NULL)
  589. return MAPI_E_INVALID_PARAMETER;
  590. HRESULT hr = HrGetMsgStore(&~ptrStore);
  591. if (hr != hrSuccess)
  592. return hr;
  593. hr = HrGetOneProp(ptrStore, PR_EC_SERVER_UID, &~ptrServerUid);
  594. if (hr != hrSuccess)
  595. return hr;
  596. if (ptrServerUid->Value.bin.lpb == NULL ||
  597. ptrServerUid->Value.bin.cb != sizeof(*lpServerUid))
  598. return MAPI_E_CORRUPT_DATA;
  599. memcpy(lpServerUid, ptrServerUid->Value.bin.lpb, sizeof *lpServerUid);
  600. return hrSuccess;
  601. }
  602. ULONG ECSyncContext::OnChange(ULONG ulFlags, LPENTRYLIST lpEntryList)
  603. {
  604. ULONG ulSyncId = 0;
  605. ULONG ulChangeId = 0;
  606. std::lock_guard<std::mutex> lock(m_hMutex);
  607. for (unsigned i = 0; i < lpEntryList->cValues; ++i) {
  608. if (lpEntryList->lpbin[i].cb < 8) {
  609. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change notification: [Invalid]");
  610. continue;
  611. }
  612. ulSyncId = SYNCID(lpEntryList->lpbin[i].lpb);
  613. ulChangeId = CHANGEID(lpEntryList->lpbin[i].lpb);
  614. m_mapNotifiedSyncIds[ulSyncId] = ulChangeId;
  615. m_lpLogger->Log(EC_LOGLEVEL_INFO, "change notification: syncid=%u, changeid=%u", ulSyncId, ulChangeId);
  616. }
  617. return 0;
  618. }