1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087 |
- /*
- * 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 <utility>
- #include <kopano/zcdefs.h>
- #include <memory>
- #include <new>
- #include <kopano/platform.h>
- #include <memory>
- #include <kopano/stringutil.h>
- #include "ics.h"
- #include "ECStoreObjectTable.h"
- #include "ECICSHelpers.h"
- #include "ECSessionManager.h"
- #include "ECMAPI.h"
- #include <mapidefs.h>
- #include <edkmdb.h>
- #include <string>
- #include <algorithm>
- #include <kopano/ECLogger.h>
- namespace KC {
- extern ECLogger* g_lpLogger;
- extern ECSessionManager* g_lpSessionManager;
- /**
- * IDbQueryCreator: Interface to the database query creators
- **/
- class IDbQueryCreator {
- public:
- virtual ~IDbQueryCreator(void) _kc_impdtor;
- virtual std::string CreateQuery() = 0;
- };
- /**
- * CommonQueryCreator: Abstract implementation of IDBQueryCreator that handles the
- * common part of all queries.
- **/
- class CommonQueryCreator : public IDbQueryCreator {
- public:
- CommonQueryCreator(unsigned int ulFlags);
-
- // IDbQueryCreator
- std::string CreateQuery(void) _kc_override;
-
- private:
- virtual std::string CreateBaseQuery() = 0;
- virtual std::string CreateOrderQuery() = 0;
-
- unsigned int m_ulFlags;
- };
- CommonQueryCreator::CommonQueryCreator(unsigned int ulFlags)
- : m_ulFlags(ulFlags)
- { }
- std::string CommonQueryCreator::CreateQuery()
- {
- std::string strQuery = CreateBaseQuery();
-
- if(!strQuery.empty()) {
-
- if ((m_ulFlags & SYNC_ASSOCIATED) == 0)
- strQuery += " AND (ISNULL(hierarchy.flags) OR hierarchy.flags & " + stringify(MSGFLAG_ASSOCIATED) + " = 0) ";
- if ((m_ulFlags & SYNC_NORMAL) == 0)
- strQuery += " AND (ISNULL(hierarchy.flags) OR hierarchy.flags & " + stringify(MSGFLAG_ASSOCIATED) + " = " + stringify(MSGFLAG_ASSOCIATED) + ") ";
- strQuery += CreateOrderQuery();
- }
-
- return strQuery;
- }
- /**
- * IncrementalQueryCreator: Creates an incremental query. In other words only messages
- * that are new or have changed since the last check will be
- * returned (deleted is a change is this context).
- **/
- class IncrementalQueryCreator _kc_final : public CommonQueryCreator {
- public:
- IncrementalQueryCreator(ECDatabase *lpDatabase, unsigned int ulSyncId, unsigned int ulChangeId, const SOURCEKEY &sFolderSourceKey, unsigned int ulFlags);
-
- private:
- std::string CreateBaseQuery(void) _kc_override;
- std::string CreateOrderQuery(void) _kc_override;
-
- ECDatabase *m_lpDatabase;
- unsigned int m_ulSyncId;
- unsigned int m_ulChangeId;
- const SOURCEKEY &m_sFolderSourceKey;
- unsigned int m_ulFlags;
- };
- IncrementalQueryCreator::IncrementalQueryCreator(ECDatabase *lpDatabase, unsigned int ulSyncId, unsigned int ulChangeId, const SOURCEKEY &sFolderSourceKey, unsigned int ulFlags)
- : CommonQueryCreator(ulFlags)
- , m_lpDatabase(lpDatabase)
- , m_ulSyncId(ulSyncId)
- , m_ulChangeId(ulChangeId)
- , m_sFolderSourceKey(sFolderSourceKey)
- , m_ulFlags(ulFlags)
- { }
-
- std::string IncrementalQueryCreator::CreateBaseQuery()
- {
- std::string strQuery;
- strQuery = "SELECT changes.id, changes.sourcekey, changes.parentsourcekey, changes.change_type, changes.flags, NULL, changes.sourcesync "
- "FROM changes ";
- if ((m_ulFlags & (SYNC_ASSOCIATED | SYNC_NORMAL)) != (SYNC_ASSOCIATED | SYNC_NORMAL))
- strQuery += "LEFT JOIN indexedproperties ON indexedproperties.val_binary = changes.sourcekey AND indexedproperties.tag = " + stringify(PROP_ID(PR_SOURCE_KEY)) + " " +
- "LEFT JOIN hierarchy ON hierarchy.id = indexedproperties.hierarchyid ";
- strQuery += "WHERE changes.id > " + stringify(m_ulChangeId) + /* Get changes from change ID N onwards */
- " AND changes.change_type & " + stringify(ICS_MESSAGE) + /* And change type is message */
- " AND changes.sourcesync != " + stringify(m_ulSyncId); /* And we didn't generate this change ourselves */
- if(!m_sFolderSourceKey.empty())
- strQuery += " AND changes.parentsourcekey = " + m_lpDatabase->EscapeBinary(m_sFolderSourceKey, m_sFolderSourceKey.size()); /* Where change took place in Folder X */
- if (m_ulFlags & SYNC_NO_DELETIONS)
- strQuery += " AND changes.change_type & " + stringify(ICS_ACTION_MASK) + " != " + stringify(ICS_SOFT_DELETE) +
- " AND changes.change_type & " + stringify(ICS_ACTION_MASK) + " != " + stringify(ICS_HARD_DELETE);
- else if (m_ulFlags & SYNC_NO_SOFT_DELETIONS)
- strQuery += " AND changes.change_type & " + stringify(ICS_ACTION_MASK) + " != " + stringify(ICS_SOFT_DELETE);
- if ((m_ulFlags & SYNC_READ_STATE) == 0)
- strQuery += " AND changes.change_type & " + stringify(ICS_ACTION_MASK) + " != " + stringify(ICS_FLAG);
-
- return strQuery;
- }
- std::string IncrementalQueryCreator::CreateOrderQuery()
- {
- return " ORDER BY changes.id";
- }
- /**
- * FullQueryCreator: Create a query that will return all messages for a sync id. The
- * messages need to be processed afterwards to see what needs to be
- * send to the client.
- **/
- class FullQueryCreator _kc_final : public CommonQueryCreator {
- public:
- FullQueryCreator(ECDatabase *lpDatabase, const SOURCEKEY &sFolderSourceKey, unsigned int ulFlags, unsigned int ulFilteredSourceSync = 0);
-
- private:
- std::string CreateBaseQuery(void) _kc_override;
- std::string CreateOrderQuery(void) _kc_override;
-
- ECDatabase *m_lpDatabase;
- const SOURCEKEY &m_sFolderSourceKey;
- unsigned int m_ulFilteredSourceSync;
- };
- FullQueryCreator::FullQueryCreator(ECDatabase *lpDatabase, const SOURCEKEY &sFolderSourceKey, unsigned int ulFlags, unsigned int ulFilteredSourceSync)
- : CommonQueryCreator(ulFlags)
- , m_lpDatabase(lpDatabase)
- , m_sFolderSourceKey(sFolderSourceKey)
- , m_ulFilteredSourceSync(ulFilteredSourceSync)
- { }
-
- std::string FullQueryCreator::CreateBaseQuery()
- {
- std::string strQuery;
- assert(!m_sFolderSourceKey.empty());
- strQuery = "SELECT changes.id as id, sourcekey.val_binary as sourcekey, parentsourcekey.val_binary, " + stringify(ICS_MESSAGE_NEW) + ", NULL, hierarchy.flags, changes.sourcesync "
- "FROM hierarchy "
- "JOIN indexedproperties as sourcekey ON sourcekey.hierarchyid = hierarchy.id AND sourcekey.tag=" + stringify(PROP_ID(PR_SOURCE_KEY)) + " "
- "JOIN indexedproperties as parentsourcekey ON parentsourcekey.hierarchyid = hierarchy.parent AND parentsourcekey.tag=" + stringify(PROP_ID(PR_SOURCE_KEY)) +
- " LEFT JOIN changes on changes.sourcekey=sourcekey.val_binary AND changes.parentsourcekey=parentsourcekey.val_binary AND changes.change_type=" + stringify(ICS_MESSAGE_NEW) + " ";
-
- strQuery += "WHERE parentsourcekey.val_binary = " + m_lpDatabase->EscapeBinary(m_sFolderSourceKey, m_sFolderSourceKey.size()) +
- " AND hierarchy.type=" + stringify(MAPI_MESSAGE) + " AND hierarchy.flags & 1024 = 0";
-
- if (m_ulFilteredSourceSync)
- strQuery += " AND (changes.sourcesync is NULL OR changes.sourcesync!=" + stringify(m_ulFilteredSourceSync) + ")";
-
- return strQuery;
- }
- std::string FullQueryCreator::CreateOrderQuery()
- {
- return " ORDER BY hierarchy.id DESC";
- }
- /**
- * NullQueryCreator: Returns no query at all. This is only used for SYNC_CATCHUP syncs that
- * do not have a restriction set. (When a restriction is set, we still need to generate the message set
- * so we cannot optimize anything out then).
- **/
- class NullQueryCreator _kc_final : public CommonQueryCreator {
- public:
- NullQueryCreator();
-
- private:
- std::string CreateBaseQuery(void) _kc_override;
- std::string CreateOrderQuery(void) _kc_override;
- };
- NullQueryCreator::NullQueryCreator() : CommonQueryCreator(SYNC_CATCHUP)
- { }
-
- std::string NullQueryCreator::CreateBaseQuery()
- {
- return std::string();
- }
- std::string NullQueryCreator::CreateOrderQuery()
- {
- return std::string();
- }
- /**
- * IMessageProcessor: Interface to the message processors.
- **/
- class IMessageProcessor {
- public:
- virtual ~IMessageProcessor(void) _kc_impdtor;
- virtual ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) = 0;
- virtual ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) = 0;
- virtual ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) = 0;
- virtual unsigned int GetMaxChangeId() const = 0;
- };
- /**
- * NonLegacyIncrementalProcessor: Processes accepted and rejected messages without the burden of tracking
- * legacy or checking for presence of messages.
- * This processor expects to be used in conjunction with the IncrementalQueryCreator,
- * which implies that all changes are genuin changes and no messages will be
- * rejected through a restriction.
- **/
- class NonLegacyIncrementalProcessor _kc_final : public IMessageProcessor {
- public:
- NonLegacyIncrementalProcessor(unsigned int ulMaxChangeId);
- ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) _kc_override;
- ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) _kc_override;
- ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) _kc_override
- {
- /* No legacy, no residuals. */
- return erSuccess;
- }
- unsigned int GetMaxChangeId(void) const _kc_override { return m_ulMaxChangeId; }
-
- private:
- unsigned int m_ulMaxChangeId;
- };
- NonLegacyIncrementalProcessor::NonLegacyIncrementalProcessor(unsigned int ulMaxChangeId)
- : m_ulMaxChangeId(ulMaxChangeId)
- { }
- ECRESULT NonLegacyIncrementalProcessor::ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags)
- {
- // Since all changes are truly new changes, we'll just set the changetype to whatever we receive
- assert(lpulChangeType != NULL);
- assert(lpDBRow != NULL && lpDBRow[icsChangeType] != NULL);
- assert(lpDBRow != NULL && lpDBRow[icsID] != NULL);
-
- *lpulChangeType = atoui(lpDBRow[icsChangeType]);
- *lpulFlags = lpDBRow[icsFlags] ? atoui(lpDBRow[icsFlags]) : 0;
- ec_log(EC_LOGLEVEL_ICS, "NonLegacyIncrementalAccepted: sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
- return erSuccess;
- }
- ECRESULT NonLegacyIncrementalProcessor::ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType)
- {
- // Since no restriction can be applied when using this processor, we'll never get a reject.
- // We'll set the changetype to 0 anyway.
- assert(false);
- *lpulChangeType = 0;
- ec_log(EC_LOGLEVEL_ICS, "NonLegacyIncrementalRejected: sourcekey=%s, changetype=0", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str());
- return erSuccess;
- }
- /**
- * NonLegacyFullProcessor: Processes accepted and rejected messages without the burden of tracking
- * legacy, but allowing messages to be processed that were synced to the
- * client previously. Since we don't have legacy, we assume all messages
- * up to the current changeId are on the client.
- **/
- class NonLegacyFullProcessor _kc_final : public IMessageProcessor {
- public:
- NonLegacyFullProcessor(unsigned int ulChangeId, unsigned int ulSyncId);
- ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) _kc_override;
- ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) _kc_override;
- ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) _kc_override
- {
- /* No legacy, no residuals. */
- return erSuccess;
- }
- unsigned int GetMaxChangeId(void) const _kc_override { return m_ulMaxChangeId; }
-
- private:
- unsigned int m_ulChangeId;
- unsigned int m_ulSyncId;
- unsigned int m_ulMaxChangeId;
- };
- NonLegacyFullProcessor::NonLegacyFullProcessor(unsigned int ulChangeId, unsigned int ulSyncId)
- : m_ulChangeId(ulChangeId)
- , m_ulSyncId(ulSyncId)
- , m_ulMaxChangeId(ulChangeId)
- { }
- ECRESULT NonLegacyFullProcessor::ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags)
- {
- // This processor will always be used with the FullQueryGenerator, which means that the provided
- // changetype is always ICS_MESSAGE_NEW. However, we do have the message flags so we can see if
- // a message is deleted.
- assert(lpulChangeType != NULL);
- assert(lpDBRow != NULL && lpDBRow[icsChangeType] != NULL && lpDBRow[icsMsgFlags] != NULL);
- assert(atoui(lpDBRow[icsChangeType]) == ICS_MESSAGE_NEW);
- unsigned int ulChange = (lpDBRow[icsID] ? atoui(lpDBRow[icsID]) : 0);
- if (atoui(lpDBRow[icsMsgFlags]) & MSGFLAG_DELETED) {
- if (ulChange <= m_ulChangeId) // Only delete if present remotely.
- *lpulChangeType = ICS_HARD_DELETE;
- } else {
- unsigned int ulSourceSync = (lpDBRow[icsSourceSync] ? atoui(lpDBRow[icsSourceSync]) : 0);
- // Only add if not present remotely and not created by the current client
- if (ulChange > m_ulChangeId && (ulSourceSync == 0 || ulSourceSync != m_ulSyncId))
- *lpulChangeType = ICS_MESSAGE_NEW;
- }
-
- *lpulFlags = 0; // Flags are only useful for ICS_FLAG
- if (ulChange > m_ulMaxChangeId)
- m_ulMaxChangeId = ulChange;
-
- ec_log(EC_LOGLEVEL_ICS, "NonLegacyFullAccepted: sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
- return erSuccess;
- }
- ECRESULT NonLegacyFullProcessor::ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType)
- {
- // We assume the client has all messages, so we need to send a delete for any non-matching message.
- assert(lpulChangeType != NULL);
- unsigned int ulChange = (lpDBRow[icsID] ? atoui(lpDBRow[icsID]) : 0);
- if (ulChange <= m_ulChangeId)
- *lpulChangeType = ICS_HARD_DELETE;
- if (ulChange > m_ulMaxChangeId)
- m_ulMaxChangeId = ulChange;
- ec_log(EC_LOGLEVEL_ICS, "NonLegacyFullRejected: sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
- return erSuccess;
- }
- /**
- * LegacyProcessor: Processes accepted and rejected messages while keeping track of legacy messages.
- **/
- class LegacyProcessor _kc_final : public IMessageProcessor {
- public:
- LegacyProcessor(unsigned int ulChangeId, unsigned int ulSyncId, const MESSAGESET &setMessages, unsigned int ulMaxFolderChange);
- ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) _kc_override;
- ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) _kc_override;
- ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) _kc_override;
- unsigned int GetMaxChangeId(void) const _kc_override { return m_ulMaxChangeId; }
-
- private:
- unsigned int m_ulSyncId;
- MESSAGESET m_setMessages;
- unsigned int m_ulMaxFolderChange;
- unsigned int m_ulMaxChangeId;
- };
- LegacyProcessor::LegacyProcessor(unsigned int ulChangeId, unsigned int ulSyncId, const MESSAGESET &setMessages, unsigned int ulMaxFolderChange)
- : m_ulSyncId(ulSyncId)
- , m_setMessages(setMessages)
- , m_ulMaxFolderChange(ulMaxFolderChange)
- , m_ulMaxChangeId(ulChangeId)
- {
- /**
- * We'll never get an empty set when a restriction was used in the previous run. However it is
- * possible that the previous run returned an empty set. In that case setMessages contains exactly
- * one entry with the sourcekey set to 0x00. If that's the case we'll just empty the set and
- * continue as usual.
- **/
- if (m_setMessages.size() == 1 && m_setMessages.find(SOURCEKEY(1, "\x00")) != m_setMessages.end())
- m_setMessages.clear();
- }
- ECRESULT LegacyProcessor::ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags)
- {
- unsigned int ulMsgFlags = 0;
- // When we get here we're accepting a message that has matched the restriction (or if there was no
- // restriction). However since we have legacy, this messages might be present already, in which
- // case we need to do nothing unless its deleted or changed since the last check.
- assert(lpulChangeType != NULL);
- assert(lpDBRow != NULL && lpDBRow[icsSourceKey] != NULL && lpDBRow[icsChangeType] && lpDBRow[icsMsgFlags] != NULL);
- assert(atoui(lpDBRow[icsChangeType]) == ICS_MESSAGE_NEW);
-
- *lpulFlags = 0;
- ulMsgFlags = atoui(lpDBRow[icsMsgFlags]);
- auto iterMessage = m_setMessages.find(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey]));
- if (iterMessage == m_setMessages.cend()) {
- // The message is not synced yet!
- unsigned int ulSourceSync = (lpDBRow[icsSourceSync] ? atoui(lpDBRow[icsSourceSync]) : 0);
- if (ulMsgFlags & MSGFLAG_DELETED || (ulSourceSync != 0 && ulSourceSync == m_ulSyncId)) // Deleted or created by current client
- *lpulChangeType = 0; // Ignore
- else
- *lpulChangeType = ICS_MESSAGE_NEW;
- ec_log(EC_LOGLEVEL_ICS, "LegacyAccepted: not synced, sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
- } else {
- // The message is synced!
- if (ulMsgFlags & MSGFLAG_DELETED) // Deleted
- *lpulChangeType = ICS_HARD_DELETE;
- else if (iterMessage->second.ulChangeTypes) { // Modified
- if(iterMessage->second.ulChangeTypes & (ICS_CHANGE_FLAG_NEW | ICS_CHANGE_FLAG_CHANGE))
- *lpulChangeType = ICS_MESSAGE_CHANGE;
- else if(iterMessage->second.ulChangeTypes & ICS_CHANGE_FLAG_FLAG) {
- *lpulChangeType = ICS_MESSAGE_FLAG;
- *lpulFlags = iterMessage->second.ulFlags;
- }
- }
- else
- *lpulChangeType = 0; // Ignore
-
- ec_log(EC_LOGLEVEL_ICS, "LegacyAccepted: synced, sourcekey=%s , changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
- m_setMessages.erase(iterMessage);
- }
-
- if (*lpulChangeType != 0)
- m_ulMaxChangeId = m_ulMaxFolderChange;
-
- return erSuccess;
- }
- ECRESULT LegacyProcessor::ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType)
- {
- // When we get here we're rejecting a message that has not-matched the restriction.
- // However since we have legacy, this messages might not be present anyway, in which
- // case we need to do nothing.
- assert(lpulChangeType != NULL);
- assert(lpDBRow != NULL && lpDBRow[icsSourceKey] != NULL && lpDBRow[icsChangeType] && lpDBRow[icsMsgFlags] != NULL);
- assert(atoui(lpDBRow[icsChangeType]) == ICS_MESSAGE_NEW);
-
- auto iterMessage = m_setMessages.find(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey]));
- if (iterMessage == m_setMessages.cend()) {
- // The message is not synced yet!
- *lpulChangeType = 0; // Ignore
- ec_log(EC_LOGLEVEL_ICS, "LegacyRejected: not synced, sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
- } else {
- // The message is synced!
- *lpulChangeType = ICS_HARD_DELETE;
- m_setMessages.erase(iterMessage);
- ec_log(EC_LOGLEVEL_ICS, "LegacyRejected: synced, sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
- }
- if (*lpulChangeType != 0)
- m_ulMaxChangeId = m_ulMaxFolderChange;
-
- return erSuccess;
- }
- ECRESULT LegacyProcessor::GetResidualMessages(LPMESSAGESET lpsetResiduals)
- {
- assert(lpsetResiduals != NULL);
- std::copy(m_setMessages.begin(), m_setMessages.end(), std::inserter(*lpsetResiduals, lpsetResiduals->begin()));
- return erSuccess;
- }
- /**
- * FirstSyncProcessor: Processes accepted and rejected messages for initial syncs. And because
- * it is the first sync we assume there are no messages on the device yet.
- **/
- class FirstSyncProcessor _kc_final : public IMessageProcessor {
- public:
- FirstSyncProcessor(unsigned int ulMaxFolderChange);
- ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) _kc_override;
- ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) _kc_override;
- ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) _kc_override
- {
- /* No legacy, no residuals. */
- return erSuccess;
- }
- unsigned int GetMaxChangeId(void) const _kc_override { return m_ulMaxFolderChange; }
-
- private:
- unsigned int m_ulMaxFolderChange;
- };
- FirstSyncProcessor::FirstSyncProcessor(unsigned int ulMaxFolderChange)
- : m_ulMaxFolderChange(ulMaxFolderChange)
- { }
- ECRESULT FirstSyncProcessor::ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags)
- {
- // This processor will always be used with the FullQueryGenerator, which means that the provided
- // changetype is always ICS_MESSAGE_NEW. However, we do have the message flags so we can see if
- // a message is deleted.
- assert(lpulChangeType != NULL);
- assert(lpDBRow != NULL && lpDBRow[icsChangeType] != NULL && lpDBRow[icsMsgFlags] != NULL);
- assert(atoui(lpDBRow[icsChangeType]) == ICS_MESSAGE_NEW);
-
- *lpulFlags = 0; // Only useful for ICS_FLAG type changes
- if (atoui(lpDBRow[icsMsgFlags]) & MSGFLAG_DELETED)
- *lpulChangeType = 0; // Ignore
- else
- *lpulChangeType = ICS_MESSAGE_NEW;
-
- ec_log(EC_LOGLEVEL_ICS, "FirstSyncAccepted: sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
- return erSuccess;
- }
- ECRESULT FirstSyncProcessor::ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType)
- {
- assert(lpulChangeType != NULL);
- *lpulChangeType = 0; // Ignore
- ec_log(EC_LOGLEVEL_ICS, "FirstSyncRejected: sourcekey=%s, changetype=0", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str());
- return erSuccess;
- }
- /**
- * ECGetContentChangesHelper definitions
- **/
- ECRESULT ECGetContentChangesHelper::Create(struct soap *soap, ECSession *lpSession, ECDatabase *lpDatabase, const SOURCEKEY &sFolderSourceKey, unsigned int ulSyncId, unsigned int ulChangeId, unsigned int ulFlags, struct restrictTable *lpsRestrict, ECGetContentChangesHelper **lppHelper)
- {
- ECRESULT er = erSuccess;
- std::unique_ptr<ECGetContentChangesHelper> lpHelper(
- new(std::nothrow) ECGetContentChangesHelper(soap, lpSession, lpDatabase,
- sFolderSourceKey, ulSyncId, ulChangeId, ulFlags, lpsRestrict));
- if (lpHelper == nullptr)
- return KCERR_NOT_ENOUGH_MEMORY;
- er = lpHelper->Init();
- if (er != erSuccess)
- return er;
- assert(lppHelper != NULL);
- *lppHelper = lpHelper.release();
- return erSuccess;
- }
- ECGetContentChangesHelper::ECGetContentChangesHelper(struct soap *soap,
- ECSession *lpSession, ECDatabase *lpDatabase,
- const SOURCEKEY &sFolderSourceKey, unsigned int ulSyncId,
- unsigned int ulChangeId, unsigned int ulFlags,
- struct restrictTable *lpsRestrict) :
- m_soap(soap), m_lpSession(lpSession), m_lpDatabase(lpDatabase),
- m_lpsRestrict(lpsRestrict), m_sFolderSourceKey(sFolderSourceKey),
- m_ulSyncId(ulSyncId), m_ulChangeId(ulChangeId), m_ulFlags(ulFlags)
- { }
- ECRESULT ECGetContentChangesHelper::Init()
- {
- ECRESULT er = erSuccess;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow;
- std::string strQuery;
- assert(m_lpDatabase != NULL);
- if (m_sFolderSourceKey.empty() && m_ulChangeId == 0 &&
- !(m_ulFlags & SYNC_CATCHUP))
- // Disallow full initial exports on server level since they are insanely large
- return KCERR_NO_SUPPORT;
- strQuery = "SELECT MAX(id) FROM changes";
- if(!m_sFolderSourceKey.empty())
- strQuery += " WHERE parentsourcekey=" + m_lpDatabase->EscapeBinary(m_sFolderSourceKey, m_sFolderSourceKey.size());
-
- er = m_lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- return er;
-
- if ((lpDBRow = m_lpDatabase->FetchRow(lpDBResult)) == NULL || lpDBRow == NULL) {
- ec_log_err("ECGetContentChangesHelper::Init(): fetchrow failed");
- return KCERR_DATABASE_ERROR;
- }
-
- if (lpDBRow[0])
- m_ulMaxFolderChange = atoui(lpDBRow[0]);
- // Here we setup the classes to delegate specific work to
- if (m_ulChangeId == 0) {
- /*
- * Initial sync
- * We want all message that were not created by the current client (m_ulSyncId).
- */
- if(m_sFolderSourceKey.empty()) {
- // Optimization: when doing SYNC_CATCHUP on a non-filtered sync, we can skip looking for any changes
- assert(m_ulFlags & SYNC_CATCHUP);
- m_lpQueryCreator = new NullQueryCreator();
- } else {
- m_lpQueryCreator = new FullQueryCreator(m_lpDatabase, m_sFolderSourceKey, m_ulFlags, m_ulSyncId);
- }
- m_lpMsgProcessor = new FirstSyncProcessor(m_ulMaxFolderChange);
- } else {
- /*
- * Incremental sync
- * We first need to determine if the previous sync was with or without
- * restriction and if a restriction is requested now.
- */
- er = GetSyncedMessages(m_ulSyncId, m_ulChangeId, &m_setLegacyMessages);
- if (er != erSuccess)
- return er;
-
- if (m_setLegacyMessages.empty()) {
- /*
- * Previous request was without restriction.
- */
- if (m_lpsRestrict == NULL) {
- /*
- * This request is also without a restriction. We can use an
- * incremental query.
- */
- m_lpQueryCreator = new IncrementalQueryCreator(m_lpDatabase, m_ulSyncId, m_ulChangeId, m_sFolderSourceKey, m_ulFlags);
- m_lpMsgProcessor = new NonLegacyIncrementalProcessor(m_ulMaxFolderChange);
- } else {
- /*
- * This request is WITH a restriction. This means the client
- * switched from using no restriction to using a restriction.
- * Note: In practice this won't happen very often.
- * We need to perform a full query to be able te decide which
- * messages match the restriction and which don't.
- * Since the previous request was without a restriction, we
- * assume all messages that were present during the last sync
- * are on the device.
- * We do want to filter all messages that were created since
- * the last sync and were created by the current client. The
- * processor should do that because that's too complex for the
- * query creator to do.
- */
- m_lpQueryCreator = new FullQueryCreator(m_lpDatabase, m_sFolderSourceKey, m_ulFlags);
- m_lpMsgProcessor = new NonLegacyFullProcessor(m_ulChangeId, m_ulSyncId);
- }
- } else {
- /*
- * The previous request was with a restriction, so we can't do an
- * incremental sync in any case, as that will only get us add's and
- * deletes for changes that happened after the last sync. But we
- * can also have adds because certain older messages might not have
- * matched the previous restriction, but do match the current (where
- * no restriction is seen as a match-all restriction).
- * We do want to filter all messages that were created since
- * the last sync and were created by the current client. The
- * processor should do that because that's too complex for the
- * query creator to do.
- */
- m_lpQueryCreator = new FullQueryCreator(m_lpDatabase, m_sFolderSourceKey, m_ulFlags);
- m_lpMsgProcessor = new LegacyProcessor(m_ulChangeId, m_ulSyncId, m_setLegacyMessages, m_ulMaxFolderChange);
- }
- }
- return erSuccess;
- }
-
- ECGetContentChangesHelper::~ECGetContentChangesHelper()
- {
- delete m_lpQueryCreator;
- delete m_lpMsgProcessor;
- }
-
- ECRESULT ECGetContentChangesHelper::QueryDatabase(DB_RESULT *lppDBResult)
- {
- ECRESULT er;
- DB_RESULT lpDBResult;
- std::string strQuery;
- unsigned int ulChanges = 0;
- assert(m_lpQueryCreator != NULL);
- strQuery = m_lpQueryCreator->CreateQuery();
-
- if(!strQuery.empty()) {
- assert(m_lpDatabase != NULL);
- er = m_lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- return er;
- } else {
- ulChanges = 0;
- }
-
- if(lpDBResult)
- ulChanges = m_lpDatabase->GetNumRows(lpDBResult) + m_setLegacyMessages.size();
- else
- ulChanges = 0;
-
- m_lpChanges = (icsChangesArray*)soap_malloc(m_soap, sizeof *m_lpChanges);
- m_lpChanges->__ptr = (icsChange*)soap_malloc(m_soap, sizeof *m_lpChanges->__ptr * ulChanges);
- m_lpChanges->__size = 0;
- assert(lppDBResult != NULL);
- *lppDBResult = std::move(lpDBResult);
- return erSuccess;
- }
- ECRESULT ECGetContentChangesHelper::ProcessRows(const std::vector<DB_ROW> &db_rows, const std::vector<DB_LENGTHS> &db_lengths)
- {
- ECRESULT er = erSuccess;
- unsigned int ulChangeType = 0;
- unsigned int ulFlags = 0;
- DB_ROW lpDBRow;
- DB_LENGTHS lpDBLen;
- std::set<SOURCEKEY> matches;
- if (m_lpsRestrict) {
- assert(m_lpSession != NULL);
- er = MatchRestrictions(db_rows, db_lengths, m_lpsRestrict, &matches);
- if (er != erSuccess)
- return er;
- }
- assert(m_lpMsgProcessor != NULL);
- for (size_t i = 0; i < db_rows.size(); ++i) {
- bool fMatch = true;
- lpDBRow = db_rows[i];
- lpDBLen = db_lengths[i];
- if (m_lpsRestrict != NULL)
- fMatch = matches.find(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])) != matches.end();
- ec_log(EC_LOGLEVEL_ICS, "Processing: %s, match=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), fMatch);
- ulChangeType = 0;
- ulFlags = 0;
- if (fMatch) {
- er = m_lpMsgProcessor->ProcessAccepted(lpDBRow, lpDBLen, &ulChangeType, &ulFlags);
- if (m_lpsRestrict != NULL)
- m_setNewMessages.insert(MESSAGESET::value_type(SOURCEKEY(lpDBLen[icsSourceKey],
- lpDBRow[icsSourceKey]), SAuxMessageData(SOURCEKEY(lpDBLen[icsParentSourceKey],
- lpDBRow[icsParentSourceKey]), ICS_CHANGE_FLAG_NEW, ulFlags)));
- } else {
- er = m_lpMsgProcessor->ProcessRejected(lpDBRow, lpDBLen, &ulChangeType);
- }
- if (er != erSuccess)
- return er;
- // If ulChangeType equals 0 we can skip this message
- if (ulChangeType == 0)
- continue;
- m_lpChanges->__ptr[m_ulChangeCnt].ulChangeId = lpDBRow[icsID] ? atoui(lpDBRow[icsID]) : 0;
- m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__ptr = (unsigned char *)soap_malloc(m_soap, lpDBLen[icsSourceKey]);
- m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__size = lpDBLen[icsSourceKey];
- memcpy(m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__ptr, lpDBRow[icsSourceKey], lpDBLen[icsSourceKey]);
- m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__ptr = (unsigned char *)soap_malloc(m_soap, lpDBLen[icsParentSourceKey]);
- m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__size = lpDBLen[icsParentSourceKey];
- memcpy(m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__ptr, lpDBRow[icsParentSourceKey], lpDBLen[icsParentSourceKey]);
- m_lpChanges->__ptr[m_ulChangeCnt].ulChangeType = ulChangeType;
- m_lpChanges->__ptr[m_ulChangeCnt].ulFlags = ulFlags;
- ++m_ulChangeCnt;
- }
- return erSuccess;
- }
- ECRESULT ECGetContentChangesHelper::ProcessResidualMessages()
- {
- ECRESULT er;
- MESSAGESET setResiduals;
- assert(m_lpMsgProcessor != NULL);
- er = m_lpMsgProcessor->GetResidualMessages(&setResiduals);
- if (er != erSuccess)
- return er;
-
- for (const auto &p : setResiduals) {
- if (p.first.size() == 1 && memcmp(p.first, "\0", 1) == 0)
- continue; // Skip empty restricted set marker,
-
- ec_log(EC_LOGLEVEL_ICS, "ProcessResidualMessages: sourcekey=%s", bin2hex(p.first).c_str());
- m_lpChanges->__ptr[m_ulChangeCnt].ulChangeId = 0;
- m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__ptr = (unsigned char *)soap_malloc(m_soap, p.first.size());
- m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__size = p.first.size();
- memcpy(m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__ptr, p.first, p.first.size());
- m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__ptr = (unsigned char *)soap_malloc(m_soap, p.second.sParentSourceKey.size());
- m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__size = p.second.sParentSourceKey.size();
- memcpy(m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__ptr, p.second.sParentSourceKey, p.second.sParentSourceKey.size());
-
- m_lpChanges->__ptr[m_ulChangeCnt].ulChangeType = ICS_HARD_DELETE;
-
- m_lpChanges->__ptr[m_ulChangeCnt].ulFlags = 0;
- ++m_ulChangeCnt;
- }
- return erSuccess;
- }
- ECRESULT ECGetContentChangesHelper::Finalize(unsigned int *lpulMaxChange, icsChangesArray **lppChanges)
- {
- ECRESULT er = erSuccess;
- std::string strQuery;
- unsigned int ulMaxChange = 0;
- unsigned int ulNewChange = 0;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow;
-
- assert(lppChanges != NULL);
- assert(lpulMaxChange != NULL);
- m_lpChanges->__size = m_ulChangeCnt;
- *lppChanges = m_lpChanges;
- assert(m_lpMsgProcessor != NULL);
- ulMaxChange = m_lpMsgProcessor->GetMaxChangeId();
- if (m_ulFlags & SYNC_NO_DB_CHANGES) {
- *lpulMaxChange = ulMaxChange;
- return er;
- }
-
- // If there were no changes and this was not the initial sync, we only need to purge all too-new-syncedmessages.
- // If this is the initial sync, we might need to write the empty restricted set marker, so we can't
- // stop doing work here. Also, if we have converted from a non-restricted to a restricted set, we have to write
- // the new set of messages, even if there are no changes.
- if (m_ulChangeCnt == 0 && m_ulChangeId > 0 && !(m_setLegacyMessages.empty() && m_lpsRestrict) ) {
- assert(ulMaxChange >= m_ulChangeId);
- *lpulMaxChange = ulMaxChange;
-
- // Delete all entries that have a changeid that are greater to the new change id.
- strQuery = "DELETE FROM syncedmessages WHERE sync_id=" + stringify(m_ulSyncId) + " AND change_id>" + stringify(ulMaxChange);
- return m_lpDatabase->DoDelete(strQuery);
- }
-
- if (ulMaxChange == m_ulChangeId) {
- /**
- * If we get here, we had at least one change but the max changeid for the server is the
- * same as the changeid in the request. This means the change was caused by either a modified
- * restriction.
- * When this happens a new changeid must be generated in order to return a unique state to the
- * client that can be used in subsequent requests. We do this by creating a dummy change in the
- * changes table.
- */
- // Bump the changeid
- strQuery = "REPLACE INTO changes (sourcekey,parentsourcekey,sourcesync) VALUES (0, " + m_lpDatabase->EscapeBinary(m_sFolderSourceKey, m_sFolderSourceKey.size()) + "," + stringify(m_ulSyncId) + ")";
- er = m_lpDatabase->DoInsert(strQuery, &ulNewChange);
- if (er != erSuccess)
- return er;
- assert(ulNewChange > ulMaxChange);
- ulMaxChange = ulNewChange;
- assert(ulMaxChange > m_ulChangeId);
- }
-
- /**
- * If a restriction is set, but the set of synced messages is empty we'll make a placeholder entry
- * so we can differentiate between having all messages and having no messages on the client.
- *
- * It's actually backwards to put in a placeholder when we have no message and put in nothing when
- * we have all messages, but having all message (because no restriction was set) never stores anything
- * in the syncedmessages table, so this scheme is compatible. On top of that, having no messages synced
- * at all is rare, having all messages isn't.
- **/
- if (m_lpsRestrict && m_setNewMessages.empty())
- m_setNewMessages.insert(MESSAGESET::value_type(SOURCEKEY(1, "\x00"), SAuxMessageData(m_sFolderSourceKey, 0, 0)));
- if (!m_setNewMessages.empty()) {
- std::set<unsigned int> setChangeIds;
-
- strQuery = "SELECT DISTINCT change_id FROM syncedmessages WHERE sync_id=" + stringify(m_ulSyncId);
- er = m_lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- return er;
- while ((lpDBRow = m_lpDatabase->FetchRow(lpDBResult))) {
- if (lpDBRow == NULL || lpDBRow[0] == NULL) {
- ec_log_err("ECGetContentChangesHelper::Finalize(): row null or column null");
- return KCERR_DATABASE_ERROR; /* this should never happen */
- }
- setChangeIds.insert(atoui(lpDBRow[0]));
- }
- if (!setChangeIds.empty()) {
- std::set<unsigned int> setDeleteIds;
-
- /* Remove obsolete states
- *
- * rules:
- * 1) Remove any states that are newer than the state that was requested
- * We do this since if the client requests state X, it can never request state X+1
- * later unless X+1 is the state that was generated from this request. We can therefore
- * remove any state > X at this point, since state X+1 will be inserted later
- * 2) Remove any states that are older than the state that was requested minus nine
- * We cannot remove state X since the client may re-request this state (eg if the export
- * failed due to network error, or if the export is interrupted before ending). We also
- * do not remove state X-9 to X-1 so that we support some sort of rollback of the client.
- * This may happen if the client is restored to an old state. In practice removing X-9 to
- * X-1 will probably not cause any real problems though, and the number 9 is pretty
- * arbitrary.
- */
- // Delete any message state that is higher than the changeset that changes were
- // requested from (rule 1)
- auto iter = setChangeIds.upper_bound(m_ulChangeId);
- if (iter != setChangeIds.cend())
- std::copy(iter, setChangeIds.end(), std::inserter(setDeleteIds, setDeleteIds.begin()));
- // Find all message states that are equal or lower than the changeset that changes were requested from
- iter = setChangeIds.lower_bound(m_ulChangeId);
- // Reverse up to nine message states (less if they do not exist)
- for (int i = 0; iter != setChangeIds.begin() && i < 9; ++i, --iter);
- // Remove message states that are older than X-9 (rule 2)
- std::copy(setChangeIds.begin(), iter, std::inserter(setDeleteIds, setDeleteIds.begin()));
- if (!setDeleteIds.empty()) {
- assert(setChangeIds.size() - setDeleteIds.size() <= 9);
- strQuery = "DELETE FROM syncedmessages WHERE sync_id=" + stringify(m_ulSyncId) + " AND change_id IN (";
- for (auto del_id : setDeleteIds) {
- strQuery.append(stringify(del_id));
- strQuery.append(1, ',');
- }
- strQuery.resize(strQuery.size() - 1); // Remove trailing ','
- strQuery.append(1, ')');
- er = m_lpDatabase->DoDelete(strQuery);
- if (er != erSuccess)
- return er;
- }
- }
-
- // Create the insert query
- strQuery = "INSERT INTO syncedmessages (sync_id,change_id,sourcekey,parentsourcekey) VALUES ";
- for (const auto &p : m_setNewMessages)
- strQuery += "(" + stringify(m_ulSyncId) + "," + stringify(ulMaxChange) + "," +
- m_lpDatabase->EscapeBinary(p.first, p.first.size()) + "," +
- m_lpDatabase->EscapeBinary(p.second.sParentSourceKey, p.second.sParentSourceKey.size()) + "),";
- strQuery.resize(strQuery.size() - 1);
- er = m_lpDatabase->DoInsert(strQuery);
- if (er != erSuccess)
- return er;
- }
-
- *lpulMaxChange = ulMaxChange;
- return erSuccess;
- }
- ECRESULT ECGetContentChangesHelper::MatchRestrictions(const std::vector<DB_ROW> &db_rows,
- const std::vector<DB_LENGTHS> &db_lengths,
- struct restrictTable *restrict, std::set<SOURCEKEY> *matches_p)
- {
- ECRESULT er = erSuccess;
- unsigned int ulObjId = 0;
- ECObjectTableList lstRows;
- ECObjectTableList::value_type sRow;
- ECODStore sODStore;
- bool fMatch = false;
- std::vector<SOURCEKEY> source_keys;
- std::map<ECsIndexProp, unsigned int> index_objs;
- struct propTagArray *lpPropTags = NULL;
- struct rowSet *lpRowSet = NULL;
- std::set<SOURCEKEY> matches;
- std::vector<unsigned int> cbdata;
- std::vector<unsigned char *> lpdata;
- std::vector<unsigned int> objectids;
- memset(&sODStore, 0, sizeof(sODStore));
- ec_log(EC_LOGLEVEL_ICS, "MatchRestrictions: matching %zu rows", db_rows.size());
- for (size_t i = 0; i < db_rows.size(); ++i) {
- lpdata.push_back(reinterpret_cast<unsigned char *>(db_rows[i][icsSourceKey]));
- cbdata.push_back(db_lengths[i][icsSourceKey]);
- }
- er = g_lpSessionManager->GetCacheManager()->GetObjectsFromProp(PROP_ID(PR_SOURCE_KEY), cbdata, lpdata, index_objs);
- if (er != erSuccess)
- goto exit;
- for (const auto &i : index_objs) {
- sRow.ulObjId = i.second;
- sRow.ulOrderId = 0;
- lstRows.push_back(sRow);
- source_keys.push_back(SOURCEKEY(i.first.cbData, reinterpret_cast<const char *>(i.first.lpData)));
- ulObjId = i.second; /* no need to split QueryRowData call per-objtype (always same) */
- }
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, NULL, NULL, NULL, &sODStore.ulObjType);
- if (er != erSuccess)
- goto exit;
- er = ECGenericObjectTable::GetRestrictPropTags(restrict, NULL, &lpPropTags);
- if (er != erSuccess)
- goto exit;
- sODStore.lpGuid = new GUID;
- er = g_lpSessionManager->GetCacheManager()->GetStore(ulObjId, &sODStore.ulStoreId, sODStore.lpGuid);
- if (er != erSuccess)
- goto exit;
- assert(m_lpSession != NULL);
- // NULL for soap, not m_soap. We'll free this ourselves
- er = ECStoreObjectTable::QueryRowData(NULL, NULL, m_lpSession, &lstRows, lpPropTags, &sODStore, &lpRowSet, false, false);
- if (er != erSuccess)
- goto exit;
- if (lpRowSet->__size < 0 ||
- static_cast<size_t>(lpRowSet->__size) != lstRows.size()) {
- er = KCERR_DATABASE_ERROR;
- ec_log_err("ECGetContentChangesHelper::MatchRestriction(): unexpected row count");
- goto exit;
- }
- for (gsoap_size_t j = 0; j < lpRowSet->__size; ++j) {
- // @todo: Get a proper locale for the case insensitive comparisons inside MatchRowRestrict
- er = ECGenericObjectTable::MatchRowRestrict(g_lpSessionManager->GetCacheManager(), &lpRowSet->__ptr[j], restrict, NULL, createLocaleFromName(""), &fMatch);
- if(er != erSuccess)
- goto exit;
- if (fMatch)
- matches.insert(source_keys[j]);
- }
- ec_log(EC_LOGLEVEL_ICS, "MatchRestrictions: %zu match(es) out of %d rows (%d properties)",
- matches.size(), lpRowSet->__size, lpPropTags->__size);
- *matches_p = std::move(matches);
- exit:
- delete sODStore.lpGuid;
- if(lpPropTags)
- FreePropTagArray(lpPropTags);
- if(lpRowSet)
- FreeRowSet(lpRowSet, true);
- return er;
- }
- ECRESULT ECGetContentChangesHelper::GetSyncedMessages(unsigned int ulSyncId, unsigned int ulChangeId, LPMESSAGESET lpsetMessages)
- {
- ECRESULT er = erSuccess;
- std::string strSubQuery;
- std::string strQuery;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow;
- DB_LENGTHS lpDBLen;
-
- strQuery =
- "SELECT m.sourcekey, m.parentsourcekey, c.change_type, c.flags "
- "FROM syncedmessages as m "
- "LEFT JOIN changes as c "
- "ON m.sourcekey=c.sourcekey AND m.parentsourcekey=c.parentsourcekey AND c.id > " + stringify(ulChangeId) + " AND c.sourcesync != " + stringify(ulSyncId) + " "
- "WHERE sync_id=" + stringify(ulSyncId) + " AND change_id=" + stringify(ulChangeId);
- assert(m_lpDatabase != NULL);
- er = m_lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- return er;
-
- while ((lpDBRow = m_lpDatabase->FetchRow(lpDBResult))) {
- lpDBLen = m_lpDatabase->FetchRowLengths(lpDBResult);
- if (lpDBRow == NULL || lpDBLen == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
- ec_log_err("ECGetContentChangesHelper::GetSyncedMessages(): row or columns null");
- return KCERR_DATABASE_ERROR; /* this should never happen */
- }
- auto iResult = lpsetMessages->insert(MESSAGESET::value_type(SOURCEKEY(lpDBLen[0], lpDBRow[0]), SAuxMessageData(SOURCEKEY(lpDBLen[1], lpDBRow[1]), 1 << (lpDBRow[2]?atoui(lpDBRow[2]):0), lpDBRow[3]?atoui(lpDBRow[3]):0)));
- if (iResult.second == false && lpDBRow[2] != nullptr)
- iResult.first->second.ulChangeTypes |= 1 << (lpDBRow[2]?atoui(lpDBRow[2]):0);
- }
- return erSuccess;
- }
- bool ECGetContentChangesHelper::CompareMessageEntry(const MESSAGESET::value_type &lhs, const MESSAGESET::value_type &rhs)
- {
- return lhs.first == rhs.first;
- }
- bool ECGetContentChangesHelper::MessageSetsDiffer() const
- {
- if (m_setLegacyMessages.size() != m_setNewMessages.size())
- return true;
-
- return !std::equal(m_setLegacyMessages.begin(), m_setLegacyMessages.end(), m_setNewMessages.begin(), &CompareMessageEntry);
- }
- } /* namespaces */
|