ECChangeAdvisor.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  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/platform.h>
  18. #include <kopano/lockhelper.hpp>
  19. #include <kopano/memory.hpp>
  20. #include <kopano/ECGuid.h>
  21. #include <kopano/ECInterfaceDefs.h>
  22. #include <ECSyncLog.h>
  23. #include <kopano/ECDebug.h>
  24. #include <kopano/ECLogger.h>
  25. #include "ECChangeAdvisor.h"
  26. #include "ECMsgStore.h"
  27. using namespace KCHL;
  28. ULONG ECChangeAdvisor::GetSyncId(const ConnectionMap::value_type &sConnection)
  29. {
  30. return sConnection.first;
  31. }
  32. ECChangeAdvisor::SyncStateMap::value_type ECChangeAdvisor::ConvertSyncState(const SSyncState &sSyncState)
  33. {
  34. return SyncStateMap::value_type(sSyncState.ulSyncId, sSyncState.ulChangeId);
  35. }
  36. SSyncState ECChangeAdvisor::ConvertSyncStateMapEntry(const SyncStateMap::value_type &sMapEntry)
  37. {
  38. SSyncState tmp = {sMapEntry.first, sMapEntry.second};
  39. return tmp;
  40. }
  41. bool ECChangeAdvisor::CompareSyncId(const ConnectionMap::value_type &sConnection, const SyncStateMap::value_type &sSyncState)
  42. {
  43. return sConnection.first < sSyncState.first;
  44. }
  45. ECChangeAdvisor::ECChangeAdvisor(ECMsgStore *lpMsgStore)
  46. : m_lpMsgStore(lpMsgStore)
  47. {
  48. ECSyncLog::GetLogger(&m_lpLogger);
  49. m_lpMsgStore->AddRef();
  50. // Need MUTEX RECURSIVE because with a reconnection the funtion PurgeStates called indirect the function Reload again:
  51. // ECChangeAdvisor::Reload(....)
  52. // WSTransport::HrReLogon()
  53. // WSTransport::HrGetSyncStates(....)
  54. // ECChangeAdvisor::PurgeStates()
  55. // ECChangeAdvisor::UpdateState(IStream * lpStream)
  56. }
  57. ECChangeAdvisor::~ECChangeAdvisor()
  58. {
  59. if (m_ulReloadId)
  60. m_lpMsgStore->lpTransport->RemoveSessionReloadCallback(m_ulReloadId);
  61. // Unregister notifications
  62. if (!(m_ulFlags & SYNC_CATCHUP))
  63. m_lpMsgStore->m_lpNotifyClient->Unadvise(ECLISTCONNECTION(m_mapConnections.begin(), m_mapConnections.end()));
  64. if (m_lpChangeAdviseSink)
  65. m_lpChangeAdviseSink->Release();
  66. if (m_lpLogger)
  67. m_lpLogger->Release();
  68. m_lpMsgStore->Release();
  69. }
  70. HRESULT ECChangeAdvisor::QueryInterface(REFIID refiid, void **lppInterface)
  71. {
  72. REGISTER_INTERFACE2(ECChangeAdvisor, this);
  73. REGISTER_INTERFACE2(ECUnknown, this);
  74. REGISTER_INTERFACE2(IECChangeAdvisor, &this->m_xECChangeAdvisor);
  75. REGISTER_INTERFACE2(IUnknown, &this->m_xECChangeAdvisor);
  76. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  77. }
  78. /**
  79. * Create a ECChangeAdvisor instance.
  80. *
  81. * @param[in] lpMsgStore The store to register the change notifications on
  82. * @param[out] lppChangeAdvisor The newly create change advisor
  83. *
  84. * @retval MAPI_E_INVALID_PARAMETER lpMsgStore or lppChangeAdvisor are NULL pointers
  85. * @retval MAPI_E_NO_SUPPORT The profile was create with notification disabled or
  86. * enhanced ICS is not enabled.
  87. */
  88. HRESULT ECChangeAdvisor::Create(ECMsgStore *lpMsgStore, ECChangeAdvisor **lppChangeAdvisor)
  89. {
  90. HRESULT hr = hrSuccess;
  91. object_ptr<ECChangeAdvisor> lpChangeAdvisor;
  92. BOOL fEnhancedICS = false;
  93. if (lpMsgStore == nullptr || lppChangeAdvisor == nullptr)
  94. return MAPI_E_INVALID_PARAMETER;
  95. if (lpMsgStore->m_lpNotifyClient == nullptr)
  96. return MAPI_E_NO_SUPPORT;
  97. hr = lpMsgStore->lpTransport->HrCheckCapabilityFlags(KOPANO_CAP_ENHANCED_ICS, &fEnhancedICS);
  98. if (hr != hrSuccess)
  99. return hr;
  100. if (!fEnhancedICS)
  101. return MAPI_E_NO_SUPPORT;
  102. lpChangeAdvisor.reset(new ECChangeAdvisor(lpMsgStore), false);
  103. hr = lpChangeAdvisor->QueryInterface(IID_ECChangeAdvisor, (void**)lppChangeAdvisor);
  104. if (hr != hrSuccess)
  105. return hr;
  106. hr = lpMsgStore->lpTransport->AddSessionReloadCallback(lpChangeAdvisor, &Reload, &lpChangeAdvisor->m_ulReloadId);
  107. if (hr != hrSuccess)
  108. return hr;
  109. lpChangeAdvisor.release();
  110. return hrSuccess;
  111. }
  112. HRESULT ECChangeAdvisor::GetLastError(HRESULT hResult, ULONG ulFlags, LPMAPIERROR *lppMAPIError)
  113. {
  114. return MAPI_E_NO_SUPPORT;
  115. }
  116. HRESULT ECChangeAdvisor::Config(LPSTREAM lpStream, LPGUID /*lpGUID*/,
  117. IECChangeAdviseSink *lpAdviseSink, ULONG ulFlags)
  118. {
  119. HRESULT hr = hrSuccess;
  120. ULONG ulVal = 0;
  121. memory_ptr<ENTRYLIST> lpEntryList;
  122. ULONG ulRead = {0};
  123. LARGE_INTEGER liSeekStart = {{0}};
  124. if (lpAdviseSink == nullptr && !(ulFlags & SYNC_CATCHUP))
  125. return MAPI_E_INVALID_PARAMETER;
  126. // Unregister notifications
  127. if (!(m_ulFlags & SYNC_CATCHUP))
  128. m_lpMsgStore->m_lpNotifyClient->Unadvise(ECLISTCONNECTION(m_mapConnections.begin(), m_mapConnections.end()));
  129. m_mapConnections.clear();
  130. if (m_lpChangeAdviseSink) {
  131. m_lpChangeAdviseSink->Release();
  132. m_lpChangeAdviseSink = NULL;
  133. }
  134. m_ulFlags = ulFlags;
  135. if (lpAdviseSink) {
  136. m_lpChangeAdviseSink = lpAdviseSink;
  137. m_lpChangeAdviseSink->AddRef();
  138. }
  139. if (lpStream == NULL)
  140. return hr;
  141. hr = lpStream->Seek(liSeekStart, SEEK_SET, NULL);
  142. if (hr != hrSuccess)
  143. return hr;
  144. hr = lpStream->Read(&ulVal, sizeof(ulVal), &ulRead);
  145. if (hr != hrSuccess)
  146. return hr;
  147. if (ulRead != sizeof(ulVal))
  148. return MAPI_E_CALL_FAILED;
  149. if (ulVal > 0) {
  150. hr = MAPIAllocateBuffer(sizeof *lpEntryList, &~lpEntryList);
  151. if (hr != hrSuccess)
  152. return hr;
  153. hr = MAPIAllocateMore(ulVal * sizeof *lpEntryList->lpbin, lpEntryList, (void**)&lpEntryList->lpbin);
  154. if (hr != hrSuccess)
  155. return hr;
  156. lpEntryList->cValues = ulVal;
  157. for (ULONG i = 0; i < lpEntryList->cValues; ++i) {
  158. hr = lpStream->Read(&ulVal, sizeof(ulVal), &ulRead);
  159. if (hr != hrSuccess)
  160. return hr;
  161. if (ulRead != sizeof(ulVal))
  162. return MAPI_E_CALL_FAILED;
  163. hr = MAPIAllocateMore(ulVal, lpEntryList, (void**)&lpEntryList->lpbin[i].lpb);
  164. if (hr != hrSuccess)
  165. return hr;
  166. lpEntryList->lpbin[i].cb = ulVal;
  167. hr = lpStream->Read(lpEntryList->lpbin[i].lpb, ulVal, &ulRead);
  168. if (hr != hrSuccess)
  169. return hr;
  170. if (ulRead != ulVal)
  171. return MAPI_E_CALL_FAILED;
  172. }
  173. hr = AddKeys(lpEntryList);
  174. if (hr != hrSuccess)
  175. return hr;
  176. }
  177. return hrSuccess;
  178. }
  179. /**
  180. * Purge states
  181. *
  182. * @note m_hConnectionLock must be locked
  183. */
  184. HRESULT ECChangeAdvisor::PurgeStates()
  185. {
  186. HRESULT hr;
  187. ECLISTSYNCID lstSyncId;
  188. ECLISTSYNCSTATE lstSyncState;
  189. SyncStateMap mapChangeId;
  190. std::list<ConnectionMap::value_type> lstObsolete;
  191. std::list<ConnectionMap::value_type>::const_iterator iterObsolete;
  192. // First get the most up to date change ids for all registered sync ids (we will ignore the changeids since we don't know if we actually got that far)
  193. std::transform(m_mapConnections.begin(), m_mapConnections.end(), std::back_inserter(lstSyncId), &GetSyncId);
  194. hr = m_lpMsgStore->m_lpNotifyClient->UpdateSyncStates(lstSyncId, &lstSyncState);
  195. if (hr != hrSuccess)
  196. return hr;
  197. // Create a map based on the returned sync states
  198. std::transform(lstSyncState.begin(), lstSyncState.end(), std::inserter(mapChangeId, mapChangeId.begin()), &ConvertSyncState);
  199. // Find all connections that are not used for the returned set of sync states and remove them
  200. std::set_difference(m_mapConnections.begin(), m_mapConnections.end(), mapChangeId.begin(), mapChangeId.end(), std::back_inserter(lstObsolete), &CompareSyncId);
  201. // Get rid of the obsolete connections (suboptimal)
  202. for (iterObsolete = lstObsolete.begin(); iterObsolete != lstObsolete.end(); ++iterObsolete) {
  203. m_lpMsgStore->m_lpNotifyClient->Unadvise(iterObsolete->second);
  204. m_mapConnections.erase(iterObsolete->first);
  205. m_mapSyncStates.erase(iterObsolete->first);
  206. }
  207. return hrSuccess;
  208. }
  209. HRESULT ECChangeAdvisor::UpdateState(LPSTREAM lpStream)
  210. {
  211. HRESULT hr = hrSuccess;
  212. LARGE_INTEGER liPos = {{0}};
  213. ULARGE_INTEGER uliSize = {{0}};
  214. ULONG ulVal = 0;
  215. SyncStateMap mapChangeId;
  216. scoped_rlock lock(m_hConnectionLock);
  217. if (m_lpChangeAdviseSink == NULL && !(m_ulFlags & SYNC_CATCHUP))
  218. return MAPI_E_UNCONFIGURED;
  219. if (lpStream == NULL)
  220. return MAPI_E_INVALID_PARAMETER;
  221. hr = PurgeStates();
  222. if (hr != hrSuccess)
  223. return hr;
  224. // Since m_mapSyncStates are related m_mapConnection the maps should
  225. // be equal in size.
  226. assert(m_mapConnections.size() == m_mapSyncStates.size());
  227. // Create the status stream
  228. lpStream->Seek(liPos, STREAM_SEEK_SET, NULL);
  229. lpStream->SetSize(uliSize);
  230. // First the amount of items in the stream
  231. ulVal = (ULONG)m_mapConnections.size();
  232. lpStream->Write(&ulVal, sizeof(ulVal), NULL);
  233. for (const auto &p : m_mapConnections) {
  234. // The size of the sync state
  235. ulVal = 2 * sizeof(ULONG); // syncid, changeid
  236. lpStream->Write(&ulVal, sizeof(ulVal), NULL);
  237. // syncid
  238. lpStream->Write(&p.first, sizeof(p.first), NULL);
  239. // changeid
  240. lpStream->Write(&m_mapSyncStates[p.first], sizeof(SyncStateMap::key_type), NULL);
  241. }
  242. return hrSuccess;
  243. }
  244. HRESULT ECChangeAdvisor::AddKeys(LPENTRYLIST lpEntryList)
  245. {
  246. HRESULT hr = hrSuccess;
  247. SSyncState *lpsSyncState = NULL;
  248. ECLISTCONNECTION listConnections;
  249. ECLISTSYNCSTATE listSyncStates;
  250. if (m_lpChangeAdviseSink == NULL && !(m_ulFlags & SYNC_CATCHUP))
  251. return MAPI_E_UNCONFIGURED;
  252. if (lpEntryList == NULL)
  253. return MAPI_E_INVALID_PARAMETER;
  254. scoped_rlock lock(m_hConnectionLock);
  255. ZLOG_DEBUG(m_lpLogger, "Adding %u keys", lpEntryList->cValues);
  256. for (ULONG i = 0; hr == hrSuccess && i < lpEntryList->cValues; ++i) {
  257. if (lpEntryList->lpbin[i].cb >= sizeof(SSyncState)) {
  258. lpsSyncState = (SSyncState*)lpEntryList->lpbin[i].lpb;
  259. ZLOG_DEBUG(m_lpLogger, " - Key %u: syncid=%u, changeid=%u", i, lpsSyncState->ulSyncId, lpsSyncState->ulChangeId);
  260. // Check if we don't have this sync state already
  261. if (m_mapConnections.find(lpsSyncState->ulSyncId) != m_mapConnections.end()) {
  262. ZLOG_DEBUG(m_lpLogger, " - Key %u: duplicate!", lpsSyncState->ulSyncId);
  263. continue;
  264. }
  265. if (!(m_ulFlags & SYNC_CATCHUP))
  266. listSyncStates.push_back(*lpsSyncState);
  267. else
  268. listConnections.push_back(ConnectionMap::value_type(lpsSyncState->ulSyncId, 0));
  269. } else {
  270. m_lpLogger->Log(EC_LOGLEVEL_ERROR, " - Key %u: Invalid size=%u", i, lpEntryList->lpbin[i].cb);
  271. hr = MAPI_E_INVALID_PARAMETER;
  272. }
  273. }
  274. if (!(m_ulFlags & SYNC_CATCHUP))
  275. hr = m_lpMsgStore->m_lpNotifyClient->Advise(listSyncStates, m_lpChangeAdviseSink, &listConnections);
  276. if (hr == hrSuccess) {
  277. m_mapConnections.insert(listConnections.begin(), listConnections.end());
  278. std::transform(listSyncStates.begin(), listSyncStates.end(), std::inserter(m_mapSyncStates, m_mapSyncStates.begin()), &ConvertSyncState);
  279. }
  280. return hr;
  281. }
  282. HRESULT ECChangeAdvisor::RemoveKeys(LPENTRYLIST lpEntryList)
  283. {
  284. HRESULT hr = hrSuccess;
  285. SSyncState *lpsSyncState = NULL;
  286. ECLISTCONNECTION listConnections;
  287. if (m_lpChangeAdviseSink == NULL && !(m_ulFlags & SYNC_CATCHUP))
  288. return MAPI_E_UNCONFIGURED;
  289. if (lpEntryList == NULL)
  290. return MAPI_E_INVALID_PARAMETER;
  291. scoped_rlock lock(m_hConnectionLock);
  292. for (ULONG i = 0; hr == hrSuccess && i < lpEntryList->cValues; ++i) {
  293. if (lpEntryList->lpbin[i].cb >= sizeof(SSyncState)) {
  294. lpsSyncState = (SSyncState*)lpEntryList->lpbin[i].lpb;
  295. // Try to delete the sync state from state map anyway
  296. m_mapSyncStates.erase(lpsSyncState->ulSyncId);
  297. // Check if we even have the sync state
  298. auto iterConnection = m_mapConnections.find(lpsSyncState->ulSyncId);
  299. if (iterConnection == m_mapConnections.cend())
  300. continue;
  301. // Unregister the sync state.
  302. if (!(m_ulFlags & SYNC_CATCHUP))
  303. listConnections.push_back(*iterConnection);
  304. // Remove from map
  305. m_mapConnections.erase(iterConnection);
  306. }
  307. }
  308. return m_lpMsgStore->m_lpNotifyClient->Unadvise(listConnections);
  309. }
  310. HRESULT ECChangeAdvisor::IsMonitoringSyncId(syncid_t ulSyncId)
  311. {
  312. if (m_mapConnections.find(ulSyncId) == m_mapConnections.end())
  313. return MAPI_E_NOT_FOUND;
  314. return hrSuccess;
  315. }
  316. HRESULT ECChangeAdvisor::UpdateSyncState(syncid_t ulSyncId, changeid_t ulChangeId)
  317. {
  318. scoped_rlock lock(m_hConnectionLock);
  319. auto iSyncState = m_mapSyncStates.find(ulSyncId);
  320. if (iSyncState == m_mapSyncStates.cend())
  321. return MAPI_E_INVALID_PARAMETER;
  322. iSyncState->second = ulChangeId;
  323. return hrSuccess;
  324. }
  325. HRESULT ECChangeAdvisor::Reload(void *lpParam, ECSESSIONID /*newSessionId*/)
  326. {
  327. HRESULT hr = hrSuccess;
  328. auto lpChangeAdvisor = static_cast<ECChangeAdvisor *>(lpParam);
  329. ECLISTSYNCSTATE listSyncStates;
  330. ECLISTCONNECTION listConnections;
  331. if (lpParam == NULL)
  332. return MAPI_E_INVALID_PARAMETER;
  333. scoped_rlock lock(lpChangeAdvisor->m_hConnectionLock);
  334. if ((lpChangeAdvisor->m_ulFlags & SYNC_CATCHUP))
  335. return hrSuccess;
  336. /**
  337. * Here we will reregister all change notifications.
  338. **/
  339. // Unregister notifications first
  340. lpChangeAdvisor->m_lpMsgStore->m_lpNotifyClient->Unadvise(ECLISTCONNECTION(lpChangeAdvisor->m_mapConnections.begin(), lpChangeAdvisor->m_mapConnections.end()));
  341. lpChangeAdvisor->m_mapConnections.clear();
  342. // Now re-register the notifications
  343. std::transform(lpChangeAdvisor->m_mapSyncStates.begin(), lpChangeAdvisor->m_mapSyncStates.end(), std::back_inserter(listSyncStates), &ConvertSyncStateMapEntry);
  344. hr = lpChangeAdvisor->m_lpMsgStore->m_lpNotifyClient->Advise(listSyncStates, lpChangeAdvisor->m_lpChangeAdviseSink, &listConnections);
  345. if (hr == hrSuccess)
  346. lpChangeAdvisor->m_mapConnections.insert(listConnections.begin(), listConnections.end());
  347. return hr;
  348. }
  349. // IECChangeAdvisor interface
  350. DEF_ULONGMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, AddRef, (void))
  351. DEF_ULONGMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, Release, (void))
  352. DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, QueryInterface, (REFIID, refiid), (void **, lppInterface))
  353. DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, GetLastError, (HRESULT, hResult), (ULONG, ulFlags), (LPMAPIERROR *, lppMAPIError))
  354. DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, Config, (LPSTREAM, lpStream), (LPGUID, lpGUID), (IECChangeAdviseSink *, lpAdviseSink), (ULONG, ulFlags))
  355. DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, UpdateState, (LPSTREAM, lpStream))
  356. DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, AddKeys, (LPENTRYLIST, lpEntryList))
  357. DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, RemoveKeys, (LPENTRYLIST, lpEntryList))
  358. DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, IsMonitoringSyncId, (syncid_t, ulSyncId))
  359. DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, UpdateSyncState, (syncid_t, ulSyncId), (changeid_t, ulChangeId))