123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <kopano/platform.h>
- #include <kopano/lockhelper.hpp>
- #include <kopano/memory.hpp>
- #include <kopano/ECGuid.h>
- #include <kopano/ECInterfaceDefs.h>
- #include <ECSyncLog.h>
- #include <kopano/ECDebug.h>
- #include <kopano/ECLogger.h>
- #include "ECChangeAdvisor.h"
- #include "ECMsgStore.h"
- using namespace KCHL;
- ULONG ECChangeAdvisor::GetSyncId(const ConnectionMap::value_type &sConnection)
- {
- return sConnection.first;
- }
- ECChangeAdvisor::SyncStateMap::value_type ECChangeAdvisor::ConvertSyncState(const SSyncState &sSyncState)
- {
- return SyncStateMap::value_type(sSyncState.ulSyncId, sSyncState.ulChangeId);
- }
- SSyncState ECChangeAdvisor::ConvertSyncStateMapEntry(const SyncStateMap::value_type &sMapEntry)
- {
- SSyncState tmp = {sMapEntry.first, sMapEntry.second};
- return tmp;
- }
- bool ECChangeAdvisor::CompareSyncId(const ConnectionMap::value_type &sConnection, const SyncStateMap::value_type &sSyncState)
- {
- return sConnection.first < sSyncState.first;
- }
- ECChangeAdvisor::ECChangeAdvisor(ECMsgStore *lpMsgStore)
- : m_lpMsgStore(lpMsgStore)
- {
- ECSyncLog::GetLogger(&m_lpLogger);
- m_lpMsgStore->AddRef();
- // Need MUTEX RECURSIVE because with a reconnection the funtion PurgeStates called indirect the function Reload again:
- // ECChangeAdvisor::Reload(....)
- // WSTransport::HrReLogon()
- // WSTransport::HrGetSyncStates(....)
- // ECChangeAdvisor::PurgeStates()
- // ECChangeAdvisor::UpdateState(IStream * lpStream)
- }
- ECChangeAdvisor::~ECChangeAdvisor()
- {
- if (m_ulReloadId)
- m_lpMsgStore->lpTransport->RemoveSessionReloadCallback(m_ulReloadId);
- // Unregister notifications
- if (!(m_ulFlags & SYNC_CATCHUP))
- m_lpMsgStore->m_lpNotifyClient->Unadvise(ECLISTCONNECTION(m_mapConnections.begin(), m_mapConnections.end()));
- if (m_lpChangeAdviseSink)
- m_lpChangeAdviseSink->Release();
-
- if (m_lpLogger)
- m_lpLogger->Release();
- m_lpMsgStore->Release();
- }
- HRESULT ECChangeAdvisor::QueryInterface(REFIID refiid, void **lppInterface)
- {
- REGISTER_INTERFACE2(ECChangeAdvisor, this);
- REGISTER_INTERFACE2(ECUnknown, this);
- REGISTER_INTERFACE2(IECChangeAdvisor, &this->m_xECChangeAdvisor);
- REGISTER_INTERFACE2(IUnknown, &this->m_xECChangeAdvisor);
- return MAPI_E_INTERFACE_NOT_SUPPORTED;
- }
- /**
- * Create a ECChangeAdvisor instance.
- *
- * @param[in] lpMsgStore The store to register the change notifications on
- * @param[out] lppChangeAdvisor The newly create change advisor
- *
- * @retval MAPI_E_INVALID_PARAMETER lpMsgStore or lppChangeAdvisor are NULL pointers
- * @retval MAPI_E_NO_SUPPORT The profile was create with notification disabled or
- * enhanced ICS is not enabled.
- */
- HRESULT ECChangeAdvisor::Create(ECMsgStore *lpMsgStore, ECChangeAdvisor **lppChangeAdvisor)
- {
- HRESULT hr = hrSuccess;
- object_ptr<ECChangeAdvisor> lpChangeAdvisor;
- BOOL fEnhancedICS = false;
- if (lpMsgStore == nullptr || lppChangeAdvisor == nullptr)
- return MAPI_E_INVALID_PARAMETER;
- if (lpMsgStore->m_lpNotifyClient == nullptr)
- return MAPI_E_NO_SUPPORT;
- hr = lpMsgStore->lpTransport->HrCheckCapabilityFlags(KOPANO_CAP_ENHANCED_ICS, &fEnhancedICS);
- if (hr != hrSuccess)
- return hr;
- if (!fEnhancedICS)
- return MAPI_E_NO_SUPPORT;
- lpChangeAdvisor.reset(new ECChangeAdvisor(lpMsgStore), false);
- hr = lpChangeAdvisor->QueryInterface(IID_ECChangeAdvisor, (void**)lppChangeAdvisor);
- if (hr != hrSuccess)
- return hr;
- hr = lpMsgStore->lpTransport->AddSessionReloadCallback(lpChangeAdvisor, &Reload, &lpChangeAdvisor->m_ulReloadId);
- if (hr != hrSuccess)
- return hr;
- lpChangeAdvisor.release();
- return hrSuccess;
- }
- HRESULT ECChangeAdvisor::GetLastError(HRESULT hResult, ULONG ulFlags, LPMAPIERROR *lppMAPIError)
- {
- return MAPI_E_NO_SUPPORT;
- }
- HRESULT ECChangeAdvisor::Config(LPSTREAM lpStream, LPGUID /*lpGUID*/,
- IECChangeAdviseSink *lpAdviseSink, ULONG ulFlags)
- {
- HRESULT hr = hrSuccess;
- ULONG ulVal = 0;
- memory_ptr<ENTRYLIST> lpEntryList;
- ULONG ulRead = {0};
- LARGE_INTEGER liSeekStart = {{0}};
- if (lpAdviseSink == nullptr && !(ulFlags & SYNC_CATCHUP))
- return MAPI_E_INVALID_PARAMETER;
- // Unregister notifications
- if (!(m_ulFlags & SYNC_CATCHUP))
- m_lpMsgStore->m_lpNotifyClient->Unadvise(ECLISTCONNECTION(m_mapConnections.begin(), m_mapConnections.end()));
- m_mapConnections.clear();
- if (m_lpChangeAdviseSink) {
- m_lpChangeAdviseSink->Release();
- m_lpChangeAdviseSink = NULL;
- }
- m_ulFlags = ulFlags;
- if (lpAdviseSink) {
- m_lpChangeAdviseSink = lpAdviseSink;
- m_lpChangeAdviseSink->AddRef();
- }
- if (lpStream == NULL)
- return hr;
- hr = lpStream->Seek(liSeekStart, SEEK_SET, NULL);
- if (hr != hrSuccess)
- return hr;
- hr = lpStream->Read(&ulVal, sizeof(ulVal), &ulRead);
- if (hr != hrSuccess)
- return hr;
- if (ulRead != sizeof(ulVal))
- return MAPI_E_CALL_FAILED;
- if (ulVal > 0) {
- hr = MAPIAllocateBuffer(sizeof *lpEntryList, &~lpEntryList);
- if (hr != hrSuccess)
- return hr;
- hr = MAPIAllocateMore(ulVal * sizeof *lpEntryList->lpbin, lpEntryList, (void**)&lpEntryList->lpbin);
- if (hr != hrSuccess)
- return hr;
- lpEntryList->cValues = ulVal;
- for (ULONG i = 0; i < lpEntryList->cValues; ++i) {
- hr = lpStream->Read(&ulVal, sizeof(ulVal), &ulRead);
- if (hr != hrSuccess)
- return hr;
- if (ulRead != sizeof(ulVal))
- return MAPI_E_CALL_FAILED;
- hr = MAPIAllocateMore(ulVal, lpEntryList, (void**)&lpEntryList->lpbin[i].lpb);
- if (hr != hrSuccess)
- return hr;
- lpEntryList->lpbin[i].cb = ulVal;
- hr = lpStream->Read(lpEntryList->lpbin[i].lpb, ulVal, &ulRead);
- if (hr != hrSuccess)
- return hr;
- if (ulRead != ulVal)
- return MAPI_E_CALL_FAILED;
- }
- hr = AddKeys(lpEntryList);
- if (hr != hrSuccess)
- return hr;
- }
- return hrSuccess;
- }
- /**
- * Purge states
- *
- * @note m_hConnectionLock must be locked
- */
- HRESULT ECChangeAdvisor::PurgeStates()
- {
- HRESULT hr;
- ECLISTSYNCID lstSyncId;
- ECLISTSYNCSTATE lstSyncState;
- SyncStateMap mapChangeId;
- std::list<ConnectionMap::value_type> lstObsolete;
- std::list<ConnectionMap::value_type>::const_iterator iterObsolete;
- // 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)
- std::transform(m_mapConnections.begin(), m_mapConnections.end(), std::back_inserter(lstSyncId), &GetSyncId);
- hr = m_lpMsgStore->m_lpNotifyClient->UpdateSyncStates(lstSyncId, &lstSyncState);
- if (hr != hrSuccess)
- return hr;
- // Create a map based on the returned sync states
- std::transform(lstSyncState.begin(), lstSyncState.end(), std::inserter(mapChangeId, mapChangeId.begin()), &ConvertSyncState);
-
- // Find all connections that are not used for the returned set of sync states and remove them
- std::set_difference(m_mapConnections.begin(), m_mapConnections.end(), mapChangeId.begin(), mapChangeId.end(), std::back_inserter(lstObsolete), &CompareSyncId);
- // Get rid of the obsolete connections (suboptimal)
- for (iterObsolete = lstObsolete.begin(); iterObsolete != lstObsolete.end(); ++iterObsolete) {
- m_lpMsgStore->m_lpNotifyClient->Unadvise(iterObsolete->second);
- m_mapConnections.erase(iterObsolete->first);
- m_mapSyncStates.erase(iterObsolete->first);
- }
- return hrSuccess;
- }
- HRESULT ECChangeAdvisor::UpdateState(LPSTREAM lpStream)
- {
- HRESULT hr = hrSuccess;
- LARGE_INTEGER liPos = {{0}};
- ULARGE_INTEGER uliSize = {{0}};
- ULONG ulVal = 0;
- SyncStateMap mapChangeId;
- scoped_rlock lock(m_hConnectionLock);
- if (m_lpChangeAdviseSink == NULL && !(m_ulFlags & SYNC_CATCHUP))
- return MAPI_E_UNCONFIGURED;
- if (lpStream == NULL)
- return MAPI_E_INVALID_PARAMETER;
- hr = PurgeStates();
- if (hr != hrSuccess)
- return hr;
- // Since m_mapSyncStates are related m_mapConnection the maps should
- // be equal in size.
- assert(m_mapConnections.size() == m_mapSyncStates.size());
- // Create the status stream
- lpStream->Seek(liPos, STREAM_SEEK_SET, NULL);
- lpStream->SetSize(uliSize);
- // First the amount of items in the stream
- ulVal = (ULONG)m_mapConnections.size();
- lpStream->Write(&ulVal, sizeof(ulVal), NULL);
- for (const auto &p : m_mapConnections) {
- // The size of the sync state
- ulVal = 2 * sizeof(ULONG); // syncid, changeid
- lpStream->Write(&ulVal, sizeof(ulVal), NULL);
- // syncid
- lpStream->Write(&p.first, sizeof(p.first), NULL);
- // changeid
- lpStream->Write(&m_mapSyncStates[p.first], sizeof(SyncStateMap::key_type), NULL);
- }
- return hrSuccess;
- }
- HRESULT ECChangeAdvisor::AddKeys(LPENTRYLIST lpEntryList)
- {
- HRESULT hr = hrSuccess;
- SSyncState *lpsSyncState = NULL;
- ECLISTCONNECTION listConnections;
- ECLISTSYNCSTATE listSyncStates;
- if (m_lpChangeAdviseSink == NULL && !(m_ulFlags & SYNC_CATCHUP))
- return MAPI_E_UNCONFIGURED;
- if (lpEntryList == NULL)
- return MAPI_E_INVALID_PARAMETER;
- scoped_rlock lock(m_hConnectionLock);
- ZLOG_DEBUG(m_lpLogger, "Adding %u keys", lpEntryList->cValues);
-
- for (ULONG i = 0; hr == hrSuccess && i < lpEntryList->cValues; ++i) {
- if (lpEntryList->lpbin[i].cb >= sizeof(SSyncState)) {
- lpsSyncState = (SSyncState*)lpEntryList->lpbin[i].lpb;
- ZLOG_DEBUG(m_lpLogger, " - Key %u: syncid=%u, changeid=%u", i, lpsSyncState->ulSyncId, lpsSyncState->ulChangeId);
- // Check if we don't have this sync state already
- if (m_mapConnections.find(lpsSyncState->ulSyncId) != m_mapConnections.end()) {
- ZLOG_DEBUG(m_lpLogger, " - Key %u: duplicate!", lpsSyncState->ulSyncId);
- continue;
- }
- if (!(m_ulFlags & SYNC_CATCHUP))
- listSyncStates.push_back(*lpsSyncState);
- else
- listConnections.push_back(ConnectionMap::value_type(lpsSyncState->ulSyncId, 0));
- } else {
- m_lpLogger->Log(EC_LOGLEVEL_ERROR, " - Key %u: Invalid size=%u", i, lpEntryList->lpbin[i].cb);
- hr = MAPI_E_INVALID_PARAMETER;
- }
- }
- if (!(m_ulFlags & SYNC_CATCHUP))
- hr = m_lpMsgStore->m_lpNotifyClient->Advise(listSyncStates, m_lpChangeAdviseSink, &listConnections);
- if (hr == hrSuccess) {
- m_mapConnections.insert(listConnections.begin(), listConnections.end());
- std::transform(listSyncStates.begin(), listSyncStates.end(), std::inserter(m_mapSyncStates, m_mapSyncStates.begin()), &ConvertSyncState);
- }
- return hr;
- }
- HRESULT ECChangeAdvisor::RemoveKeys(LPENTRYLIST lpEntryList)
- {
- HRESULT hr = hrSuccess;
- SSyncState *lpsSyncState = NULL;
- ECLISTCONNECTION listConnections;
- if (m_lpChangeAdviseSink == NULL && !(m_ulFlags & SYNC_CATCHUP))
- return MAPI_E_UNCONFIGURED;
- if (lpEntryList == NULL)
- return MAPI_E_INVALID_PARAMETER;
- scoped_rlock lock(m_hConnectionLock);
-
- for (ULONG i = 0; hr == hrSuccess && i < lpEntryList->cValues; ++i) {
- if (lpEntryList->lpbin[i].cb >= sizeof(SSyncState)) {
- lpsSyncState = (SSyncState*)lpEntryList->lpbin[i].lpb;
- // Try to delete the sync state from state map anyway
- m_mapSyncStates.erase(lpsSyncState->ulSyncId);
- // Check if we even have the sync state
- auto iterConnection = m_mapConnections.find(lpsSyncState->ulSyncId);
- if (iterConnection == m_mapConnections.cend())
- continue;
- // Unregister the sync state.
- if (!(m_ulFlags & SYNC_CATCHUP))
- listConnections.push_back(*iterConnection);
- // Remove from map
- m_mapConnections.erase(iterConnection);
- }
- }
- return m_lpMsgStore->m_lpNotifyClient->Unadvise(listConnections);
- }
- HRESULT ECChangeAdvisor::IsMonitoringSyncId(syncid_t ulSyncId)
- {
- if (m_mapConnections.find(ulSyncId) == m_mapConnections.end())
- return MAPI_E_NOT_FOUND;
- return hrSuccess;
- }
- HRESULT ECChangeAdvisor::UpdateSyncState(syncid_t ulSyncId, changeid_t ulChangeId)
- {
- scoped_rlock lock(m_hConnectionLock);
- auto iSyncState = m_mapSyncStates.find(ulSyncId);
- if (iSyncState == m_mapSyncStates.cend())
- return MAPI_E_INVALID_PARAMETER;
- iSyncState->second = ulChangeId;
- return hrSuccess;
- }
- HRESULT ECChangeAdvisor::Reload(void *lpParam, ECSESSIONID /*newSessionId*/)
- {
- HRESULT hr = hrSuccess;
- auto lpChangeAdvisor = static_cast<ECChangeAdvisor *>(lpParam);
- ECLISTSYNCSTATE listSyncStates;
- ECLISTCONNECTION listConnections;
- if (lpParam == NULL)
- return MAPI_E_INVALID_PARAMETER;
- scoped_rlock lock(lpChangeAdvisor->m_hConnectionLock);
- if ((lpChangeAdvisor->m_ulFlags & SYNC_CATCHUP))
- return hrSuccess;
- /**
- * Here we will reregister all change notifications.
- **/
- // Unregister notifications first
- lpChangeAdvisor->m_lpMsgStore->m_lpNotifyClient->Unadvise(ECLISTCONNECTION(lpChangeAdvisor->m_mapConnections.begin(), lpChangeAdvisor->m_mapConnections.end()));
- lpChangeAdvisor->m_mapConnections.clear();
-
- // Now re-register the notifications
- std::transform(lpChangeAdvisor->m_mapSyncStates.begin(), lpChangeAdvisor->m_mapSyncStates.end(), std::back_inserter(listSyncStates), &ConvertSyncStateMapEntry);
- hr = lpChangeAdvisor->m_lpMsgStore->m_lpNotifyClient->Advise(listSyncStates, lpChangeAdvisor->m_lpChangeAdviseSink, &listConnections);
- if (hr == hrSuccess)
- lpChangeAdvisor->m_mapConnections.insert(listConnections.begin(), listConnections.end());
- return hr;
- }
- // IECChangeAdvisor interface
- DEF_ULONGMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, AddRef, (void))
- DEF_ULONGMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, Release, (void))
- DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, QueryInterface, (REFIID, refiid), (void **, lppInterface))
- DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, GetLastError, (HRESULT, hResult), (ULONG, ulFlags), (LPMAPIERROR *, lppMAPIError))
- DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, Config, (LPSTREAM, lpStream), (LPGUID, lpGUID), (IECChangeAdviseSink *, lpAdviseSink), (ULONG, ulFlags))
- DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, UpdateState, (LPSTREAM, lpStream))
- DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, AddKeys, (LPENTRYLIST, lpEntryList))
- DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, RemoveKeys, (LPENTRYLIST, lpEntryList))
- DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, IsMonitoringSyncId, (syncid_t, ulSyncId))
- DEF_HRMETHOD1(TRACE_MAPI, ECChangeAdvisor, ECChangeAdvisor, UpdateSyncState, (syncid_t, ulSyncId), (changeid_t, ulChangeId))
|