ECNotifyClient.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  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. #include <kopano/platform.h>
  17. #include <new>
  18. #include <stdexcept>
  19. #include <utility>
  20. #include <kopano/lockhelper.hpp>
  21. #include <kopano/memory.hpp>
  22. #include <mapispi.h>
  23. #include <mapix.h>
  24. #include <kopano/ECDebug.h>
  25. #include "ECMsgStore.h"
  26. #include "ECNotifyClient.h"
  27. #include "ECSessionGroupManager.h"
  28. #include <kopano/ECGuid.h>
  29. #include "SOAPUtils.h"
  30. #include "WSUtil.h"
  31. #include <kopano/Util.h>
  32. #include <kopano/stringutil.h>
  33. #include <kopano/mapiext.h>
  34. #define MAX_NOTIFS_PER_CALL 64
  35. struct ECADVISE {
  36. ULONG cbKey;
  37. BYTE *lpKey;
  38. ULONG ulEventMask;
  39. IMAPIAdviseSink *lpAdviseSink;
  40. ULONG ulConnection;
  41. GUID guid;
  42. ULONG ulSupportConnection;
  43. };
  44. struct ECCHANGEADVISE {
  45. ULONG ulSyncId;
  46. ULONG ulChangeId;
  47. ULONG ulEventMask;
  48. IECChangeAdviseSink *lpAdviseSink;
  49. ULONG ulConnection;
  50. GUID guid;
  51. };
  52. using namespace KCHL;
  53. static inline std::pair<ULONG,ULONG> SyncAdviseToConnection(const SSyncAdvise &sSyncAdvise) {
  54. return std::make_pair(sSyncAdvise.sSyncState.ulSyncId,sSyncAdvise.ulConnection);
  55. }
  56. ECNotifyClient::ECNotifyClient(ULONG ulProviderType, void *lpProvider,
  57. ULONG ulFlags, LPMAPISUP lpSupport) :
  58. ECUnknown("ECNotifyClient"), m_lpSupport(lpSupport),
  59. m_lpProvider(lpProvider), m_ulProviderType(ulProviderType)
  60. {
  61. TRACE_MAPI(TRACE_ENTRY, "ECNotifyClient::ECNotifyClient","");
  62. ECSESSIONID ecSessionId;
  63. if(m_ulProviderType == MAPI_STORE)
  64. m_lpTransport = ((ECMsgStore*)m_lpProvider)->lpTransport;
  65. else if(m_ulProviderType == MAPI_ADDRBOOK)
  66. m_lpTransport = ((ECABLogon*)m_lpProvider)->m_lpTransport;
  67. else
  68. throw std::runtime_error("Unknown m_ulProviderType");
  69. /* Get the sessiongroup ID of the provider that we will be handling notifications for */
  70. if (m_lpTransport->HrGetSessionId(&ecSessionId, &m_ecSessionGroupId) != hrSuccess)
  71. throw std::runtime_error("ECNotifyClient/HrGetSessionId failed");
  72. /* Get the session group that this session belongs to */
  73. if (g_ecSessionManager.GetSessionGroupData(m_ecSessionGroupId, m_lpTransport->GetProfileProps(), &m_lpSessionGroup) != hrSuccess)
  74. throw std::runtime_error("ECNotifyClient/GetSessionGroupData failed");
  75. if (m_lpSessionGroup->GetOrCreateNotifyMaster(&m_lpNotifyMaster) != hrSuccess)
  76. throw std::runtime_error("ECNotifyClient/GetOrCreateNotifyMaster failed");
  77. m_lpNotifyMaster->AddSession(this);
  78. }
  79. ECNotifyClient::~ECNotifyClient()
  80. {
  81. TRACE_MAPI(TRACE_ENTRY, "ECNotifyClient::~ECNotifyClient","");
  82. if (m_lpNotifyMaster)
  83. m_lpNotifyMaster->ReleaseSession(this);
  84. if (m_lpSessionGroup)
  85. m_lpSessionGroup->Release();
  86. /*
  87. * We MAY have been the last person using the session group. Tell the session group manager
  88. * to look at the session group and delete it if necessary
  89. */
  90. g_ecSessionManager.DeleteSessionGroupDataIfOrphan(m_ecSessionGroupId);
  91. /*
  92. * Clean up, this map should actually be empty if all advised were correctly unadvised.
  93. * This is however not always the case, but ECNotifyMaster and Server will remove all
  94. * advises when the session is removed.
  95. */
  96. ulock_rec biglock(m_hMutex);
  97. for (const auto &i : m_mapAdvise) {
  98. if (i.second->lpAdviseSink != NULL)
  99. i.second->lpAdviseSink->Release();
  100. MAPIFreeBuffer(i.second);
  101. }
  102. m_mapAdvise.clear();
  103. for (const auto &i : m_mapChangeAdvise) {
  104. if (i.second->lpAdviseSink != NULL)
  105. i.second->lpAdviseSink->Release();
  106. MAPIFreeBuffer(i.second);
  107. }
  108. m_mapChangeAdvise.clear();
  109. biglock.unlock();
  110. TRACE_MAPI(TRACE_RETURN, "ECNotifyClient::~ECNotifyClient","");
  111. }
  112. HRESULT ECNotifyClient::Create(ULONG ulProviderType, void *lpProvider, ULONG ulFlags, LPMAPISUP lpSupport, ECNotifyClient**lppNotifyClient)
  113. {
  114. auto lpNotifyClient = new(std::nothrow) ECNotifyClient(ulProviderType,
  115. lpProvider, ulFlags, lpSupport);
  116. if (lpNotifyClient == nullptr)
  117. return MAPI_E_NOT_ENOUGH_MEMORY;
  118. HRESULT hr = lpNotifyClient->QueryInterface(IID_ECNotifyClient, (void **)lppNotifyClient);
  119. if (hr != hrSuccess)
  120. delete lpNotifyClient;
  121. return hr;
  122. }
  123. HRESULT ECNotifyClient::QueryInterface(REFIID refiid, void **lppInterface)
  124. {
  125. REGISTER_INTERFACE2(ECNotifyClient, this);
  126. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  127. }
  128. /**
  129. * Register an advise connection
  130. *
  131. * In windows, registers using the IMAPISupport subscription model, so that threading is handled correctly
  132. * concerning the multithreading model selected by the client when doing MAPIInitialize(). However, when
  133. * bSynchronous is TRUE, that notification is always handled internal synchronously while the notifications
  134. * are being received.
  135. *
  136. * @param[in] cbKey Bytes in lpKey
  137. * @param[in] lpKey Key to subscribe for
  138. * @param[in] ulEventMask Mask for events to receive
  139. * @param[in] TRUE if the notification should be handled synchronously, FALSE otherwise. In linux, handled as if
  140. * it were always TRUE
  141. * @param[in] lpAdviseSink Sink to send notifications to
  142. * @param[out] lpulConnection Connection ID for the subscription
  143. * @return result
  144. */
  145. HRESULT ECNotifyClient::RegisterAdvise(ULONG cbKey, LPBYTE lpKey, ULONG ulEventMask, bool bSynchronous, LPMAPIADVISESINK lpAdviseSink, ULONG *lpulConnection)
  146. {
  147. HRESULT hr = MAPI_E_NO_SUPPORT;
  148. memory_ptr<ECADVISE> pEcAdvise;
  149. ULONG ulConnection = 0;
  150. if (lpKey == nullptr)
  151. return MAPI_E_INVALID_PARAMETER;
  152. hr = MAPIAllocateBuffer(sizeof(ECADVISE), &~pEcAdvise);
  153. if (hr != hrSuccess)
  154. return hr;
  155. *lpulConnection = 0;
  156. memset(pEcAdvise, 0, sizeof(ECADVISE));
  157. pEcAdvise->lpKey = NULL;
  158. pEcAdvise->cbKey = cbKey;
  159. hr = MAPIAllocateMore(cbKey, pEcAdvise, (LPVOID*)&pEcAdvise->lpKey);
  160. if (hr != hrSuccess)
  161. return hr;
  162. memcpy(pEcAdvise->lpKey, lpKey, cbKey);
  163. pEcAdvise->lpAdviseSink = lpAdviseSink;
  164. pEcAdvise->ulEventMask = ulEventMask;
  165. pEcAdvise->ulSupportConnection = 0;
  166. /*
  167. * Request unique connection id from Master.
  168. */
  169. hr = m_lpNotifyMaster->ReserveConnection(&ulConnection);
  170. if(hr != hrSuccess)
  171. return hr;
  172. // Add reference on the notify sink
  173. lpAdviseSink->AddRef();
  174. #ifdef NOTIFY_THROUGH_SUPPORT_OBJECT
  175. memory_ptr<NOTIFKEY> lpKeySupport;
  176. if(!bSynchronous) {
  177. hr = MAPIAllocateBuffer(CbNewNOTIFKEY(sizeof(GUID)), &~lpKeySupport);
  178. if(hr != hrSuccess)
  179. return hr;
  180. lpKeySupport->cb = sizeof(GUID);
  181. hr = CoCreateGuid((GUID *)lpKeySupport->ab);
  182. if(hr != hrSuccess)
  183. return hr;
  184. // Get support object connection id
  185. hr = m_lpSupport->Subscribe(lpKeySupport, (ulEventMask&~fnevLongTermEntryIDs), 0, lpAdviseSink, &pEcAdvise->ulSupportConnection);
  186. if(hr != hrSuccess)
  187. return hr;
  188. memcpy(&pEcAdvise->guid, lpKeySupport->ab, sizeof(GUID));
  189. }
  190. #endif
  191. {
  192. scoped_rlock biglock(m_hMutex);
  193. m_mapAdvise.insert(ECMAPADVISE::value_type(ulConnection, pEcAdvise.release()));
  194. }
  195. // Since we're ready to receive notifications now, register ourselves with the master
  196. hr = m_lpNotifyMaster->ClaimConnection(this, &ECNotifyClient::Notify, ulConnection);
  197. if(hr != hrSuccess)
  198. return hr;
  199. // Set out value
  200. *lpulConnection = ulConnection;
  201. return hrSuccess;
  202. }
  203. HRESULT ECNotifyClient::RegisterChangeAdvise(ULONG ulSyncId, ULONG ulChangeId,
  204. IECChangeAdviseSink *lpChangeAdviseSink, ULONG *lpulConnection)
  205. {
  206. HRESULT hr = MAPI_E_NO_SUPPORT;
  207. memory_ptr<ECCHANGEADVISE> pEcAdvise;
  208. ULONG ulConnection = 0;
  209. hr = MAPIAllocateBuffer(sizeof(ECCHANGEADVISE), &~pEcAdvise);
  210. if (hr != hrSuccess)
  211. return hr;
  212. *lpulConnection = 0;
  213. memset(pEcAdvise, 0, sizeof(ECCHANGEADVISE));
  214. pEcAdvise->ulSyncId = ulSyncId;
  215. pEcAdvise->ulChangeId = ulChangeId;
  216. pEcAdvise->lpAdviseSink = lpChangeAdviseSink;
  217. pEcAdvise->ulEventMask = fnevKopanoIcsChange;
  218. /*
  219. * Request unique connection id from Master.
  220. */
  221. hr = m_lpNotifyMaster->ReserveConnection(&ulConnection);
  222. if(hr != hrSuccess)
  223. return hr;
  224. /*
  225. * Setup our maps to receive the notifications
  226. */
  227. {
  228. scoped_rlock biglock(m_hMutex);
  229. lpChangeAdviseSink->AddRef();
  230. m_mapChangeAdvise.insert(ECMAPCHANGEADVISE::value_type(ulConnection, pEcAdvise.release()));
  231. }
  232. // Since we're ready to receive notifications now, register ourselves with the master
  233. hr = m_lpNotifyMaster->ClaimConnection(this, &ECNotifyClient::NotifyChange, ulConnection);
  234. if(hr != hrSuccess)
  235. return hr;
  236. // Set out value
  237. *lpulConnection = ulConnection;
  238. return hrSuccess;
  239. }
  240. HRESULT ECNotifyClient::UnRegisterAdvise(ULONG ulConnection)
  241. {
  242. /*
  243. * Release connection from Master
  244. */
  245. HRESULT hr = m_lpNotifyMaster->DropConnection(ulConnection);
  246. if (hr != hrSuccess)
  247. return hr;
  248. // Remove notify from list
  249. scoped_rlock lock(m_hMutex);
  250. auto iIterAdvise = m_mapAdvise.find(ulConnection);
  251. if (iIterAdvise != m_mapAdvise.cend()) {
  252. if(iIterAdvise->second->ulSupportConnection)
  253. m_lpSupport->Unsubscribe(iIterAdvise->second->ulSupportConnection);
  254. if (iIterAdvise->second->lpAdviseSink != NULL)
  255. iIterAdvise->second->lpAdviseSink->Release();
  256. MAPIFreeBuffer(iIterAdvise->second);
  257. m_mapAdvise.erase(iIterAdvise);
  258. return hr;
  259. }
  260. auto iIterChangeAdvise = m_mapChangeAdvise.find(ulConnection);
  261. if (iIterChangeAdvise == m_mapChangeAdvise.cend())
  262. return hr;
  263. if (iIterChangeAdvise->second->lpAdviseSink != NULL)
  264. iIterChangeAdvise->second->lpAdviseSink->Release();
  265. MAPIFreeBuffer(iIterChangeAdvise->second);
  266. m_mapChangeAdvise.erase(iIterChangeAdvise);
  267. return hr;
  268. }
  269. HRESULT ECNotifyClient::Advise(ULONG cbKey, LPBYTE lpKey, ULONG ulEventMask, LPMAPIADVISESINK lpAdviseSink, ULONG *lpulConnection){
  270. TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::Advise", "");
  271. HRESULT hr = MAPI_E_NO_SUPPORT;
  272. ULONG ulConnection = 0;
  273. hr = RegisterAdvise(cbKey, lpKey, ulEventMask, false, lpAdviseSink, &ulConnection);
  274. if (hr != hrSuccess)
  275. goto exit;
  276. //Request the advice
  277. hr = m_lpTransport->HrSubscribe(cbKey, lpKey, ulConnection, ulEventMask);
  278. if(hr != hrSuccess) {
  279. UnRegisterAdvise(ulConnection);
  280. hr = MAPI_E_NO_SUPPORT;
  281. goto exit;
  282. }
  283. // Set out value
  284. *lpulConnection = ulConnection;
  285. hr = hrSuccess;
  286. exit:
  287. TRACE_NOTIFY(TRACE_RETURN, "ECNotifyClient::Advise", "hr=0x%08X connection=%d", hr, *lpulConnection);
  288. return hr;
  289. }
  290. HRESULT ECNotifyClient::Advise(const ECLISTSYNCSTATE &lstSyncStates,
  291. IECChangeAdviseSink *lpChangeAdviseSink, ECLISTCONNECTION *lplstConnections)
  292. {
  293. TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::AdviseICS", "");
  294. HRESULT hr = MAPI_E_NO_SUPPORT;
  295. ECLISTSYNCADVISE lstAdvises;
  296. for (const auto &state : lstSyncStates) {
  297. SSyncAdvise sSyncAdvise = {{0}};
  298. hr = RegisterChangeAdvise(state.ulSyncId, state.ulChangeId, lpChangeAdviseSink, &sSyncAdvise.ulConnection);
  299. if (hr != hrSuccess)
  300. goto exit;
  301. sSyncAdvise.sSyncState = state;
  302. lstAdvises.push_back(std::move(sSyncAdvise));
  303. }
  304. hr = m_lpTransport->HrSubscribeMulti(lstAdvises, fnevKopanoIcsChange);
  305. if (hr != hrSuccess) {
  306. // On failure we'll try the one-at-a-time approach.
  307. for (auto iSyncAdvise = lstAdvises.cbegin();
  308. iSyncAdvise != lstAdvises.cend(); ++iSyncAdvise) {
  309. hr = m_lpTransport->HrSubscribe(iSyncAdvise->sSyncState.ulSyncId, iSyncAdvise->sSyncState.ulChangeId, iSyncAdvise->ulConnection, fnevKopanoIcsChange);
  310. if (hr != hrSuccess) {
  311. // Unadvise all advised connections
  312. // No point in attempting the multi version as SubscribeMulti also didn't work
  313. for (auto iSyncUnadvise = lstAdvises.cbegin();
  314. iSyncUnadvise != iSyncAdvise; ++iSyncUnadvise)
  315. m_lpTransport->HrUnSubscribe(iSyncUnadvise->ulConnection);
  316. hr = MAPI_E_NO_SUPPORT;
  317. goto exit;
  318. }
  319. }
  320. }
  321. std::transform(lstAdvises.begin(), lstAdvises.end(), std::back_inserter(*lplstConnections), &SyncAdviseToConnection);
  322. exit:
  323. if (hr != hrSuccess) {
  324. // Unregister all advises.
  325. for (auto iSyncAdvise = lstAdvises.cbegin();
  326. iSyncAdvise != lstAdvises.cend(); ++iSyncAdvise)
  327. UnRegisterAdvise(iSyncAdvise->ulConnection);
  328. }
  329. TRACE_NOTIFY(TRACE_RETURN, "ECNotifyClient::AdviseICS", "hr=0x%08X", hr);
  330. return hr;
  331. }
  332. HRESULT ECNotifyClient::Unadvise(ULONG ulConnection)
  333. {
  334. TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::Unadvise", "%d", ulConnection);
  335. HRESULT hr = MAPI_E_NO_SUPPORT;
  336. // Logoff the advisor
  337. hr = m_lpTransport->HrUnSubscribe(ulConnection);
  338. if (hr != hrSuccess)
  339. goto exit;
  340. hr = UnRegisterAdvise(ulConnection);
  341. if (hr != hrSuccess)
  342. goto exit;
  343. exit:
  344. TRACE_NOTIFY(TRACE_RETURN, "ECNotifyClient::Unadvise", "hr=0x%08X", hr);
  345. return hr;
  346. }
  347. HRESULT ECNotifyClient::Unadvise(const ECLISTCONNECTION &lstConnections)
  348. {
  349. TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::Unadvise", "");
  350. HRESULT hr = MAPI_E_NO_SUPPORT;
  351. HRESULT hrTmp;
  352. bool bWithErrors = false;
  353. // Logoff the advisors
  354. hr = m_lpTransport->HrUnSubscribeMulti(lstConnections);
  355. if (hr != hrSuccess) {
  356. hr = hrSuccess;
  357. for (const auto &p : lstConnections) {
  358. hrTmp = m_lpTransport->HrUnSubscribe(p.second);
  359. if (FAILED(hrTmp))
  360. bWithErrors = true;
  361. }
  362. }
  363. for (const auto &p : lstConnections) {
  364. hrTmp = UnRegisterAdvise(p.second);
  365. if (FAILED(hrTmp))
  366. bWithErrors = true;
  367. }
  368. if (SUCCEEDED(hr) && bWithErrors)
  369. hr = MAPI_W_ERRORS_RETURNED;
  370. TRACE_NOTIFY(TRACE_RETURN, "ECNotifyClient::Unadvise", "hr=0x%08X", hr);
  371. return hr;
  372. }
  373. // Re-registers a notification on the server. Normally only called if the server
  374. // session has been reset.
  375. HRESULT ECNotifyClient::Reregister(ULONG ulConnection, ULONG cbKey, LPBYTE lpKey)
  376. {
  377. scoped_rlock biglock(m_hMutex);
  378. ECMAPADVISE::const_iterator iter = m_mapAdvise.find(ulConnection);
  379. if (iter == m_mapAdvise.cend())
  380. return MAPI_E_NOT_FOUND;
  381. if(cbKey) {
  382. // Update key if required, when the new key is equal or smaller
  383. // then the previous key we don't need to allocate anything.
  384. // Note that we cannot do MAPIFreeBuffer() since iter->second->lpKey
  385. // was allocated with MAPIAllocateMore().
  386. if (cbKey > iter->second->cbKey) {
  387. HRESULT hr = MAPIAllocateMore(cbKey, iter->second,
  388. reinterpret_cast<void **>(&iter->second->lpKey));
  389. if (hr != hrSuccess)
  390. return hr;
  391. }
  392. memcpy(iter->second->lpKey, lpKey, cbKey);
  393. iter->second->cbKey = cbKey;
  394. }
  395. return m_lpTransport->HrSubscribe(iter->second->cbKey,
  396. iter->second->lpKey, ulConnection, iter->second->ulEventMask);
  397. }
  398. HRESULT ECNotifyClient::ReleaseAll()
  399. {
  400. scoped_rlock biglock(m_hMutex);
  401. for (auto &p : m_mapAdvise) {
  402. p.second->lpAdviseSink->Release();
  403. p.second->lpAdviseSink = NULL;
  404. }
  405. return hrSuccess;
  406. }
  407. typedef std::list<NOTIFICATION *> NOTIFICATIONLIST;
  408. typedef std::list<SBinary *> BINARYLIST;
  409. HRESULT ECNotifyClient::NotifyReload()
  410. {
  411. HRESULT hr = hrSuccess;
  412. struct notification notif;
  413. struct notificationTable table;
  414. NOTIFYLIST notifications;
  415. memset(&notif, 0, sizeof(notif));
  416. memset(&table, 0, sizeof(table));
  417. notif.ulEventType = fnevTableModified;
  418. notif.tab = &table;
  419. notif.tab->ulTableEvent = TABLE_RELOAD;
  420. notifications.push_back(&notif);
  421. // The transport used for this notifyclient *may* have a broken session. Inform the
  422. // transport that the session may be broken and it should verify that all is well.
  423. // Disabled because deadlock, research needed
  424. //m_lpTransport->HrEnsureSession();
  425. // Don't send the notification while we are locked
  426. scoped_rlock biglock(m_hMutex);
  427. for (const auto &p : m_mapAdvise)
  428. if (p.second->cbKey == 4)
  429. Notify(p.first, notifications);
  430. return hr;
  431. }
  432. HRESULT ECNotifyClient::Notify(ULONG ulConnection, const NOTIFYLIST &lNotifications)
  433. {
  434. HRESULT hr = hrSuccess;
  435. ECMAPADVISE::const_iterator iterAdvise;
  436. NOTIFICATIONLIST notifications;
  437. for (auto notp : lNotifications) {
  438. LPNOTIFICATION tmp = NULL;
  439. hr = CopySOAPNotificationToMAPINotification(m_lpProvider, notp, &tmp);
  440. if (hr != hrSuccess)
  441. continue;
  442. TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::Notify", "id=%d\n%s", notp->ulConnection, NotificationToString(1, tmp).c_str());
  443. notifications.push_back(tmp);
  444. }
  445. ulock_rec biglock(m_hMutex);
  446. /* Search for the right connection */
  447. iterAdvise = m_mapAdvise.find(ulConnection);
  448. if (iterAdvise == m_mapAdvise.cend() ||
  449. iterAdvise->second->lpAdviseSink == NULL) {
  450. TRACE_NOTIFY(TRACE_WARNING, "ECNotifyClient::Notify", "Unknown Notification id %d", ulConnection);
  451. goto exit;
  452. }
  453. if (!notifications.empty()) {
  454. /* Send notifications in batches of MAX_NOTIFS_PER_CALL notifications */
  455. auto iterNotification = notifications.cbegin();
  456. while (iterNotification != notifications.cend()) {
  457. memory_ptr<NOTIFICATION> lpNotifs;
  458. /* Create a straight array of all the notifications */
  459. hr = MAPIAllocateBuffer(sizeof(NOTIFICATION) * MAX_NOTIFS_PER_CALL, &~lpNotifs);
  460. if (hr != hrSuccess)
  461. continue;
  462. ULONG i = 0;
  463. while (iterNotification != notifications.cend() && i < MAX_NOTIFS_PER_CALL) {
  464. /* We can do a straight memcpy here because pointers are still intact */
  465. memcpy(&lpNotifs[i++], *iterNotification, sizeof(NOTIFICATION));
  466. ++iterNotification;
  467. }
  468. /* Send notification to the listener */
  469. if (!iterAdvise->second->ulSupportConnection) {
  470. if (iterAdvise->second->lpAdviseSink->OnNotify(i, lpNotifs) != 0)
  471. TRACE_NOTIFY(TRACE_WARNING, "ECNotifyClient::Notify", "Error by notify a client");
  472. } else {
  473. memory_ptr<NOTIFKEY> lpKey;
  474. ULONG ulResult = 0;
  475. hr = MAPIAllocateBuffer(CbNewNOTIFKEY(sizeof(GUID)), &~lpKey);
  476. if (hr != hrSuccess)
  477. goto exit;
  478. lpKey->cb = sizeof(GUID);
  479. memcpy(lpKey->ab, &iterAdvise->second->guid, sizeof(GUID));
  480. // FIXME log errors
  481. m_lpSupport->Notify(lpKey, i, lpNotifs, &ulResult);
  482. }
  483. }
  484. }
  485. exit:
  486. biglock.unlock();
  487. /* Release all notifications */
  488. for (auto notp : notifications)
  489. MAPIFreeBuffer(notp);
  490. return hr;
  491. }
  492. HRESULT ECNotifyClient::NotifyChange(ULONG ulConnection, const NOTIFYLIST &lNotifications)
  493. {
  494. HRESULT hr = hrSuccess;
  495. memory_ptr<ENTRYLIST> lpSyncStates;
  496. ECMAPCHANGEADVISE::const_iterator iterAdvise;
  497. BINARYLIST syncStates;
  498. ulock_rec biglock(m_hMutex, std::defer_lock_t());
  499. /* Create a straight array of MAX_NOTIFS_PER_CALL sync states */
  500. hr = MAPIAllocateBuffer(sizeof *lpSyncStates, &~lpSyncStates);
  501. if (hr != hrSuccess)
  502. return hr;
  503. memset(lpSyncStates, 0, sizeof *lpSyncStates);
  504. hr = MAPIAllocateMore(sizeof *lpSyncStates->lpbin * MAX_NOTIFS_PER_CALL, lpSyncStates, (void**)&lpSyncStates->lpbin);
  505. if (hr != hrSuccess)
  506. return hr;
  507. memset(lpSyncStates->lpbin, 0, sizeof *lpSyncStates->lpbin * MAX_NOTIFS_PER_CALL);
  508. for (auto notp : lNotifications) {
  509. LPSBinary tmp = NULL;
  510. hr = CopySOAPChangeNotificationToSyncState(notp, &tmp, lpSyncStates);
  511. if (hr != hrSuccess)
  512. continue;
  513. TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::NotifyChange", "id=%d\n%s", notp->ulConnection, bin2hex(tmp->cb, tmp->lpb).c_str());
  514. syncStates.push_back(tmp);
  515. }
  516. /* Search for the right connection */
  517. biglock.lock();
  518. iterAdvise = m_mapChangeAdvise.find(ulConnection);
  519. if (iterAdvise == m_mapChangeAdvise.cend() ||
  520. iterAdvise->second->lpAdviseSink == NULL) {
  521. TRACE_NOTIFY(TRACE_WARNING, "ECNotifyClient::NotifyChange", "Unknown Notification id %d", ulConnection);
  522. return hr;
  523. }
  524. if (!syncStates.empty()) {
  525. /* Send notifications in batches of MAX_NOTIFS_PER_CALL notifications */
  526. auto iterSyncStates = syncStates.cbegin();
  527. while (iterSyncStates != syncStates.cend()) {
  528. lpSyncStates->cValues = 0;
  529. while (iterSyncStates != syncStates.cend() &&
  530. lpSyncStates->cValues < MAX_NOTIFS_PER_CALL) {
  531. /* We can do a straight memcpy here because pointers are still intact */
  532. memcpy(&lpSyncStates->lpbin[lpSyncStates->cValues++], *iterSyncStates, sizeof *lpSyncStates->lpbin);
  533. ++iterSyncStates;
  534. }
  535. /* Send notification to the listener */
  536. if (iterAdvise->second->lpAdviseSink->OnNotify(0, lpSyncStates) != 0)
  537. TRACE_NOTIFY(TRACE_WARNING, "ECNotifyClient::NotifyChange", "Error by notify a client");
  538. }
  539. }
  540. return hrSuccess;
  541. }
  542. HRESULT ECNotifyClient::UpdateSyncStates(const ECLISTSYNCID &lstSyncId, ECLISTSYNCSTATE *lplstSyncState)
  543. {
  544. return m_lpTransport->HrGetSyncStates(lstSyncId, lplstSyncState);
  545. }