diff options
-rw-r--r-- | README.rst | 19 | ||||
-rw-r--r-- | src/syncevo/Cmdline.cpp | 28 | ||||
-rw-r--r-- | src/syncevo/LocalTransportAgent.cpp | 18 | ||||
-rw-r--r-- | src/syncevo/SyncConfig.cpp | 40 | ||||
-rw-r--r-- | src/syncevo/SyncConfig.h | 3 | ||||
-rw-r--r-- | src/syncevo/SyncContext.cpp | 89 | ||||
-rw-r--r-- | src/syncevo/SyncML.cpp | 105 | ||||
-rw-r--r-- | src/syncevo/SyncML.h | 58 | ||||
-rw-r--r-- | test/ClientTest.cpp | 105 | ||||
-rw-r--r-- | test/ClientTest.h | 22 | ||||
-rwxr-xr-x | test/test-dbus.py | 7 |
11 files changed, 370 insertions, 124 deletions
@@ -415,9 +415,9 @@ the right database dumps for the selected sources. A restore tries to minimize the number of item changes (see section `Item Changes and Data Changes`_). This means that items that are identical before and after the change will not be transmitted anew to -the server during the next synchronization. If the server somehow -needs to get a clean copy of all items on the client then, use "--sync -refresh-from-client" in the next run. :: +the peer during the next synchronization. If the peer somehow +needs to get a clean copy of all local items, then use ``--sync +refresh-from-local`` in the next run. :: syncevolution --print-items <config> <source> syncevolution [--delimiter <string>] --export <dir>|<file>|- <config> <source> [<luid> ...] @@ -451,17 +451,12 @@ a list of valid values. --sync|-s <mode>|? Temporarily synchronize the active sources in that mode. Useful - for a `refresh-from-server` or `refresh-from-client` sync which + for a `refresh-from-local` or `refresh-from-remote` sync which clears all data at one end and copies all items from the other. - **Warning:** in local sync (`CalDAV and CardDAV`_/ActiveSync, ...) and - direct sync with a phone, the sync is started by the side which acts - as server. Therefore the ``from-server`` variants - (``one-way-from-server``, ``refresh-from-server``) transfer data - from the sync config into the target config (see "Synchronization - beyond SyncML" below) resp. to a phone. The ``from-client`` variants - transfer in the other direction, even if the target config happens - to access data on a remote server. + **Warning:** `local` is the data accessed via the sync config + directly and `remote` is the data on the peer, regardless + where the data is actually stored physically. --print-servers|--print-configs|--print-peers Prints the names of all configured peers to stdout. There is no diff --git a/src/syncevo/Cmdline.cpp b/src/syncevo/Cmdline.cpp index 077396c9..0bcaf6e6 100644 --- a/src/syncevo/Cmdline.cpp +++ b/src/syncevo/Cmdline.cpp @@ -1464,7 +1464,7 @@ bool Cmdline::run() { context->getSyncSources()) { boost::shared_ptr<PersistentSyncSourceConfig> source_config = context->getSyncSourceConfig(source); - if (source_config->getSync() != "disabled") { + if (!source_config->isDisabled()) { context->setConfigFilter(false, source, m_props.createSourceFilter(m_server, source)); } } @@ -3159,7 +3159,7 @@ protected: TestCmdline failure2("--sync", "foo", NULL); CPPUNIT_ASSERT(!failure2.m_cmdline->parse()); CPPUNIT_ASSERT_EQUAL_DIFF("", failure2.m_out.str()); - CPPUNIT_ASSERT_EQUAL(string("ERROR: '--sync foo': not one of the valid values (two-way, slow, refresh-from-client = refresh-client, refresh-from-server = refresh-server = refresh, one-way-from-client = one-way-client, one-way-from-server = one-way-server = one-way, disabled = none)\n"), lastLine(failure2.m_err.str())); + CPPUNIT_ASSERT_EQUAL(string("ERROR: '--sync foo': not one of the valid values (two-way, slow, refresh-from-local, refresh-from-remote = refresh, one-way-from-local, one-way-from-remote = one-way, refresh-from-client = refresh-client, refresh-from-server = refresh-server, one-way-from-client = one-way-client, one-way-from-server = one-way-server, disabled = none)\n"), lastLine(failure2.m_err.str())); TestCmdline help("--sync", " ?", NULL); help.doit(); @@ -3170,19 +3170,23 @@ protected: " only send/receive changes since last sync\n" " slow\n" " exchange all items\n" - " refresh-from-client\n" - " discard all remote items and replace with the items on the client\n" - " refresh-from-server\n" - " discard all local items and replace with the items on the server\n" - " one-way-from-client\n" - " transmit changes from client\n" - " one-way-from-server\n" - " transmit changes from server\n" + " refresh-from-remote\n" + " discard all local items and replace with\n" + " the items on the peer\n" + " refresh-from-local\n" + " discard all items on the peer and replace\n" + " with the local items\n" + " one-way-from-remote\n" + " transmit changes from peer\n" + " one-way-from-local\n" + " transmit local changes\n" " disabled (or none)\n" " synchronization disabled\n" " \n" - " **WARNING**: which side is `client` and which is `server` depends on\n" - " the value of the ``peerIsClient`` property in the configuration.\n" + " refresh/one-way-from-server/client are also supported. Their use is\n" + " discouraged because the direction of the data transfer depends\n" + " on the role of the local side (can be server or client), which is\n" + " not always obvious.\n" " \n" " When accepting a sync session in a SyncML server (HTTP server), only\n" " sources with sync != disabled are made available to the client,\n" diff --git a/src/syncevo/LocalTransportAgent.cpp b/src/syncevo/LocalTransportAgent.cpp index 4e4c43c2..82834b08 100644 --- a/src/syncevo/LocalTransportAgent.cpp +++ b/src/syncevo/LocalTransportAgent.cpp @@ -284,7 +284,7 @@ void LocalTransportAgent::run() BOOST_FOREACH(const string &sourceName, m_server->getSyncSources()) { SyncSourceNodes nodes = m_server->getSyncSourceNodesNoTracking(sourceName); SyncSourceConfig source(sourceName, nodes); - string sync = source.getSync(); + std::string sync = source.getSync(); if (sync != "disabled") { string targetName = source.getURINonEmpty(); SyncSourceNodes targetNodes = m_client->getSyncSourceNodes(targetName); @@ -304,13 +304,23 @@ void LocalTransportAgent::run() // so none of the regular config nodes have to // be written. If a sync mode was set, it must have been // done before in this loop => error in original config. - if (string(targetSource.getSync()) != "disabled") { + if (!targetSource.isDisabled()) { m_client->throwError(StringPrintf("%s: source targetted twice by %s", fullTargetName.c_str(), m_clientContext.c_str())); } - targetSource.setSync(sync.c_str(), true); - targetSource.setURI(sourceName.c_str(), true); + // invert data direction + if (sync == "refresh-from-local") { + sync = "refresh-from-remote"; + } else if (sync == "refresh-from-remote") { + sync = "refresh-from-local"; + } else if (sync == "one-way-from-local") { + sync = "one-way-from-remote"; + } else if (sync == "one-way-from-remote") { + sync = "one-way-from-local"; + } + targetSource.setSync(sync, true); + targetSource.setURI(sourceName, true); } } diff --git a/src/syncevo/SyncConfig.cpp b/src/syncevo/SyncConfig.cpp index 1590fa89..b0b60d92 100644 --- a/src/syncevo/SyncConfig.cpp +++ b/src/syncevo/SyncConfig.cpp @@ -2153,20 +2153,24 @@ StringConfigProperty SyncSourceConfig::m_sourcePropSync("sync", " only send/receive changes since last sync\n" " slow\n" " exchange all items\n" - " refresh-from-client\n" - " discard all remote items and replace with the items on the client\n" - " refresh-from-server\n" - " discard all local items and replace with the items on the server\n" - " one-way-from-client\n" - " transmit changes from client\n" - " one-way-from-server\n" - " transmit changes from server\n" + " refresh-from-remote\n" + " discard all local items and replace with\n" + " the items on the peer\n" + " refresh-from-local\n" + " discard all items on the peer and replace\n" + " with the local items\n" + " one-way-from-remote\n" + " transmit changes from peer\n" + " one-way-from-local\n" + " transmit local changes\n" " disabled (or none)\n" - " synchronization disabled\n\n" - - "**WARNING**: which side is `client` and which is `server` depends on\n" - "the value of the ``peerIsClient`` property in the configuration.\n\n" - + " synchronization disabled\n" + "\n" + "refresh/one-way-from-server/client are also supported. Their use is\n" + "discouraged because the direction of the data transfer depends\n" + "on the role of the local side (can be server or client), which is\n" + "not always obvious.\n" + "\n" "When accepting a sync session in a SyncML server (HTTP server), only\n" "sources with sync != disabled are made available to the client,\n" "which chooses the final sync mode based on its own configuration.\n" @@ -2178,10 +2182,16 @@ StringConfigProperty SyncSourceConfig::m_sourcePropSync("sync", Values() + (Aliases("two-way")) + (Aliases("slow")) + + + (Aliases("refresh-from-local")) + + (Aliases("refresh-from-remote") + "refresh") + + (Aliases("one-way-from-local")) + + (Aliases("one-way-from-remote") + "one-way") + + (Aliases("refresh-from-client") + "refresh-client") + - (Aliases("refresh-from-server") + "refresh-server" + "refresh") + + (Aliases("refresh-from-server") + "refresh-server") + (Aliases("one-way-from-client") + "one-way-client") + - (Aliases("one-way-from-server") + "one-way-server" + "one-way") + + (Aliases("one-way-from-server") + "one-way-server") + (Aliases("disabled") + "none")); static class SourceBackendConfigProperty : public StringConfigProperty { diff --git a/src/syncevo/SyncConfig.h b/src/syncevo/SyncConfig.h index a3e98634..4930ad4b 100644 --- a/src/syncevo/SyncConfig.h +++ b/src/syncevo/SyncConfig.h @@ -1985,6 +1985,9 @@ class SyncSourceConfig { virtual std::string getSync() const; virtual void setSync(const std::string &value, bool temporarily = false); + /** shortcut for checking sync mode against "disabled" */ + bool isDisabled() { return getSync() == "disabled"; } + private: std::string m_name; SyncSourceNodes m_nodes; diff --git a/src/syncevo/SyncContext.cpp b/src/syncevo/SyncContext.cpp index 6ceb4a0a..233d3179 100644 --- a/src/syncevo/SyncContext.cpp +++ b/src/syncevo/SyncContext.cpp @@ -1577,8 +1577,9 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type, extra2=1 for resumed session, extra3 0=twoway, 1=fromserver, 2=fromclient */ // -1 is used for alerting a restore from backup. Synthesis won't use this + bool peerIsClient = getPeerIsClient(); if (extra1 != -1) { - SE_LOG_INFO(NULL, NULL, "%s: %s %s sync%s", + SE_LOG_INFO(NULL, NULL, "%s: %s %s sync%s (%s)", source.getDisplayName().c_str(), extra2 ? "resuming" : "starting", extra1 == 0 ? "normal" : @@ -1588,31 +1589,34 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type, extra3 == 0 ? ", two-way" : extra3 == 1 ? " from server" : extra3 == 2 ? " from client" : - ", unknown direction"); + ", unknown direction", + peerIsClient ? "peer is client" : "peer is server"); - SyncMode mode = SYNC_NONE; + SimpleSyncMode mode = SIMPLE_SYNC_NONE; + std::string sync = source.getSync(); switch (extra1) { case 0: switch (extra3) { case 0: - mode = SYNC_TWO_WAY; + mode = SIMPLE_SYNC_TWO_WAY; if (m_serverMode && m_serverAlerted && - source.getSync() == "one-way-from-server") { + (sync == "one-way-from-server" || + sync == "one-way-from-local")) { // As in the slow/refresh-from-server case below, // pretending to do a two-way incremental sync // is a correct way of executing the requested // one-way sync, as long as the client doesn't // send any of its own changes. The Synthesis // engine does that. - mode = SYNC_ONE_WAY_FROM_SERVER; + mode = SIMPLE_SYNC_ONE_WAY_FROM_LOCAL; } break; case 1: - mode = SYNC_ONE_WAY_FROM_SERVER; + mode = peerIsClient ? SIMPLE_SYNC_ONE_WAY_FROM_LOCAL : SIMPLE_SYNC_ONE_WAY_FROM_REMOTE; break; case 2: - mode = SYNC_ONE_WAY_FROM_CLIENT; + mode = peerIsClient ? SIMPLE_SYNC_ONE_WAY_FROM_REMOTE : SIMPLE_SYNC_ONE_WAY_FROM_LOCAL; break; } break; @@ -1620,28 +1624,29 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type, case 2: switch (extra3) { case 0: - mode = SYNC_SLOW; + mode = SIMPLE_SYNC_SLOW; if (m_serverMode && m_serverAlerted && - source.getSync() == "refresh-from-server") { + (sync == "refresh-from-server" || + sync == "refresh-from-local")) { // We run as server and told the client to refresh // its data. A slow sync is how some clients (the // Synthesis engine included) execute that sync mode; // let's be optimistic and assume that the client // did as it was told and deleted its data. - mode = SYNC_REFRESH_FROM_SERVER; + mode = SIMPLE_SYNC_REFRESH_FROM_LOCAL; } break; case 1: - mode = SYNC_REFRESH_FROM_SERVER; + mode = peerIsClient ? SIMPLE_SYNC_REFRESH_FROM_LOCAL : SIMPLE_SYNC_REFRESH_FROM_REMOTE; break; case 2: - mode = SYNC_REFRESH_FROM_CLIENT; + mode = peerIsClient ? SIMPLE_SYNC_REFRESH_FROM_REMOTE : SIMPLE_SYNC_REFRESH_FROM_LOCAL; break; } break; } - source.recordFinalSyncMode(mode); + source.recordFinalSyncMode(SyncMode(mode)); source.recordFirstSync(extra1 == 2); source.recordResumeSync(extra2 == 1); } else { @@ -1759,7 +1764,8 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type, // refresh-from-server/client. That's a matter of // taste. In SyncEvolution we'd like these // items to show up, so add it here. - source.getFinalSyncMode() == (m_serverMode ? SYNC_REFRESH_FROM_CLIENT : SYNC_REFRESH_FROM_SERVER) ? + (source.getFinalSyncMode() == (m_serverMode ? SYNC_REFRESH_FROM_CLIENT : SYNC_REFRESH_FROM_SERVER) || + source.getFinalSyncMode() == SYNC_REFRESH_FROM_REMOTE) ? source.getNumDeleted() : extra3); break; @@ -1950,10 +1956,8 @@ void SyncContext::initSources(SourceList &sourceList) BOOST_FOREACH(const string &name, configuredSources) { boost::shared_ptr<PersistentSyncSourceConfig> sc(getSyncSourceConfig(name)); SyncSourceNodes source = getSyncSourceNodes (name); - // is the source enabled? - string sync = sc->getSync(); - bool enabled = sync != "disabled"; - if (enabled) { + std::string sync = sc->getSync(); + if (sync != "disabled") { SourceType sourceType = SyncSource::getSourceType(source); if (sourceType.m_backend == "virtual") { //This is a virtual sync source, check and enable the referenced @@ -1995,11 +1999,7 @@ void SyncContext::initSources(SourceList &sourceList) boost::shared_ptr<PersistentSyncSourceConfig> sc(getSyncSourceConfig(name)); SyncSourceNodes source = getSyncSourceNodes (name); - - // is the source enabled? - string sync = sc->getSync(); - bool enabled = sync != "disabled"; - if (enabled) { + if (!sc->isDisabled()) { SourceType sourceType = SyncSource::getSourceType(source); if (sourceType.m_backend != "virtual") { SyncSourceParams params(name, @@ -2430,9 +2430,13 @@ void SyncContext::getConfigXML(string &xml, string &configname) // we *want* a slow sync, but couldn't tell the client -> force it server-side datastores << " <alertscript> FORCESLOWSYNC(); </alertscript>\n"; } else if (mode != "slow" && - mode != "refresh-from-server" && // is implemented as "delete local data" + "slow sync", - // so a slow sync is acceptable in this case + // slow-sync detection not implemented when running as server, + // not even when initiating the sync (direct sync with phone) !m_serverMode && + // is implemented as "delete local data" + "slow sync", + // so a slow sync is acceptable in this case + mode != "refresh-from-server" && + mode != "refresh-from-remote" && // The forceSlow should be disabled if the sync session is // initiated by a remote peer (eg. Server Alerted Sync) !m_remoteInitiated && @@ -3012,11 +3016,11 @@ bool SyncContext::sendSAN(uint16_t version) BOOST_FOREACH (boost::shared_ptr<VirtualSyncSource> vSource, m_sourceListPtr->getVirtualSources()) { std::string evoSyncSource = vSource->getDatabaseID(); std::string sync = vSource->getSync(); - int mode = StringToSyncMode (sync, true); + SANSyncMode mode = AlertSyncMode(StringToSyncMode(sync, true), getPeerIsClient()); std::vector<std::string> mappedSources = unescapeJoinedString (evoSyncSource, ','); BOOST_FOREACH (std::string source, mappedSources) { dataSources.erase (source); - if (mode == SYNC_SLOW) { + if (mode == SA_SLOW) { // We force a source which the client is not expected to use into slow mode. // Shouldn't we rather reject attempts to synchronize it? (*m_sourceListPtr)[source]->setForceSlowSync(true); @@ -3025,19 +3029,19 @@ bool SyncContext::sendSAN(uint16_t version) dataSources.insert (vSource->getName()); } - int syncMode = 0; + SANSyncMode syncMode = SA_INVALID; vector<pair <string, string> > alertedSources; /* For each source to be notified do the following: */ BOOST_FOREACH (string name, dataSources) { boost::shared_ptr<PersistentSyncSourceConfig> sc(getSyncSourceConfig(name)); string sync = sc->getSync(); - int mode = StringToSyncMode (sync, true); - if (mode == SYNC_SLOW) { + SANSyncMode mode = AlertSyncMode(StringToSyncMode(sync, true), getPeerIsClient()); + if (mode == SA_SLOW) { (*m_sourceListPtr)[name]->setForceSlowSync(true); - mode = SA_SYNC_TWO_WAY; + mode = SA_TWO_WAY; } - if (mode <SYNC_FIRST || mode >SYNC_LAST) { + if (mode < SA_FIRST || mode > SA_LAST) { SE_LOG_DEV (NULL, NULL, "Ignoring data source %s with an invalid sync mode", name.c_str()); continue; } @@ -3274,27 +3278,30 @@ SyncMLStatus SyncContext::doSync() m_engine.SetInt32Value(target, "enabled", 1); int slow = 0; int direction = 0; - string mode = source->getSync(); - if (!strcasecmp(mode.c_str(), "slow")) { + string sync = source->getSync(); + // this code only runs when we are the client, + // take that into account for the "from-local/remote" modes + SimpleSyncMode mode = SimplifySyncMode(StringToSyncMode(sync), false); + if (mode == SIMPLE_SYNC_SLOW) { slow = 1; direction = 0; - } else if (!strcasecmp(mode.c_str(), "two-way")) { + } else if (mode == SIMPLE_SYNC_TWO_WAY) { slow = 0; direction = 0; - } else if (!strcasecmp(mode.c_str(), "refresh-from-server")) { + } else if (mode == SIMPLE_SYNC_REFRESH_FROM_REMOTE) { slow = 1; direction = 1; - } else if (!strcasecmp(mode.c_str(), "refresh-from-client")) { + } else if (mode == SIMPLE_SYNC_REFRESH_FROM_LOCAL) { slow = 1; direction = 2; - } else if (!strcasecmp(mode.c_str(), "one-way-from-server")) { + } else if (mode == SIMPLE_SYNC_ONE_WAY_FROM_REMOTE) { slow = 0; direction = 1; - } else if (!strcasecmp(mode.c_str(), "one-way-from-client")) { + } else if (mode == SIMPLE_SYNC_ONE_WAY_FROM_LOCAL) { slow = 0; direction = 2; } else { - source->throwError(string("invalid sync mode: ") + mode); + source->throwError(string("invalid sync mode: ") + sync); } m_engine.SetInt32Value(target, "forceslow", slow); m_engine.SetInt32Value(target, "syncmode", direction); diff --git a/src/syncevo/SyncML.cpp b/src/syncevo/SyncML.cpp index f152b829..78214c77 100644 --- a/src/syncevo/SyncML.cpp +++ b/src/syncevo/SyncML.cpp @@ -38,6 +38,95 @@ using namespace std; SE_BEGIN_CXX +SimpleSyncMode SimplifySyncMode(SyncMode mode, bool peerIsClient) +{ + switch (mode) { + case SYNC_NONE: + case SYNC_TWO_WAY: + case SYNC_SLOW: + case SYNC_RESTORE_FROM_BACKUP: + case SYNC_ONE_WAY_FROM_LOCAL: + case SYNC_REFRESH_FROM_LOCAL: + case SYNC_ONE_WAY_FROM_REMOTE: + case SYNC_REFRESH_FROM_REMOTE: + return static_cast<SimpleSyncMode>(mode); + + case SA_SYNC_ONE_WAY_FROM_CLIENT: + case SYNC_ONE_WAY_FROM_CLIENT: + return peerIsClient ? SIMPLE_SYNC_ONE_WAY_FROM_REMOTE : SIMPLE_SYNC_ONE_WAY_FROM_LOCAL; + + case SA_SYNC_REFRESH_FROM_CLIENT: + case SYNC_REFRESH_FROM_CLIENT: + return peerIsClient ? SIMPLE_SYNC_REFRESH_FROM_REMOTE : SIMPLE_SYNC_REFRESH_FROM_LOCAL; + + case SA_SYNC_ONE_WAY_FROM_SERVER: + case SYNC_ONE_WAY_FROM_SERVER: + return peerIsClient ? SIMPLE_SYNC_ONE_WAY_FROM_LOCAL : SIMPLE_SYNC_ONE_WAY_FROM_REMOTE; + + case SA_SYNC_REFRESH_FROM_SERVER: + case SYNC_REFRESH_FROM_SERVER: + return peerIsClient ? SIMPLE_SYNC_REFRESH_FROM_LOCAL : SIMPLE_SYNC_REFRESH_FROM_REMOTE; + + case SA_SYNC_TWO_WAY: + return SIMPLE_SYNC_TWO_WAY; + + case SYNC_LAST: + case SYNC_INVALID: + return SIMPLE_SYNC_INVALID; + } + + return SIMPLE_SYNC_INVALID; +} + +SANSyncMode AlertSyncMode(SyncMode mode, bool peerIsClient) +{ + switch(mode) { + case SYNC_RESTORE_FROM_BACKUP: + case SYNC_NONE: + case SYNC_LAST: + case SYNC_INVALID: + return SA_INVALID; + + case SYNC_SLOW: + return SA_SLOW; + + case SYNC_TWO_WAY: + case SA_SYNC_TWO_WAY: + return SA_TWO_WAY; + + case SYNC_ONE_WAY_FROM_CLIENT: + case SA_SYNC_ONE_WAY_FROM_CLIENT: + return SA_ONE_WAY_FROM_CLIENT; + + case SYNC_REFRESH_FROM_CLIENT: + case SA_SYNC_REFRESH_FROM_CLIENT: + return SA_REFRESH_FROM_CLIENT; + + case SYNC_ONE_WAY_FROM_SERVER: + case SA_SYNC_ONE_WAY_FROM_SERVER: + return SA_ONE_WAY_FROM_SERVER; + + case SYNC_REFRESH_FROM_SERVER: + case SA_SYNC_REFRESH_FROM_SERVER: + return SA_REFRESH_FROM_SERVER; + + case SYNC_ONE_WAY_FROM_LOCAL: + return peerIsClient ? SA_ONE_WAY_FROM_SERVER : SA_ONE_WAY_FROM_CLIENT; + + case SYNC_REFRESH_FROM_LOCAL: + return peerIsClient ? SA_REFRESH_FROM_SERVER : SA_REFRESH_FROM_CLIENT; + + case SYNC_ONE_WAY_FROM_REMOTE: + return peerIsClient ? SA_ONE_WAY_FROM_CLIENT : SA_ONE_WAY_FROM_SERVER; + + case SYNC_REFRESH_FROM_REMOTE: + return peerIsClient ? SA_REFRESH_FROM_CLIENT : SA_REFRESH_FROM_SERVER; + } + + return SA_INVALID; +} + + std::string PrettyPrintSyncMode(SyncMode mode, bool userVisible) { switch (mode) { @@ -62,6 +151,14 @@ std::string PrettyPrintSyncMode(SyncMode mode, bool userVisible) return userVisible ? "refresh-from-server" : "SYNC_REFRESH_FROM_SERVER"; case SYNC_RESTORE_FROM_BACKUP: return userVisible ? "restore-from-backup" : "SYNC_RESTORE_FROM_BACKUP"; + case SYNC_ONE_WAY_FROM_LOCAL: + return userVisible ? "one-way-from-local" : "SYNC_REFRESH_FROM_LOCAL"; + case SYNC_REFRESH_FROM_LOCAL: + return userVisible ? "refresh-from-local" : "SYNC_REFRESH_FROM_LOCAL"; + case SYNC_ONE_WAY_FROM_REMOTE: + return userVisible ? "one-way-from-remote" : "SYNC_ONE_WAY_FROM_REMOTE"; + case SYNC_REFRESH_FROM_REMOTE: + return userVisible ? "refresh-from-remote" : "SYNC_REFRESH_FROM_REMOTE"; default: std::stringstream res; @@ -84,6 +181,14 @@ SyncMode StringToSyncMode(const std::string &mode, bool serverAlerted) return serverAlerted? SA_SYNC_ONE_WAY_FROM_SERVER: SYNC_ONE_WAY_FROM_SERVER; } else if (boost::iequals(mode, "one-way-from-client") || boost::iequals(mode, "SYNC_ONE_WAY_FROM_CLIENT")) { return serverAlerted? SA_SYNC_ONE_WAY_FROM_CLIENT: SYNC_ONE_WAY_FROM_CLIENT; + } else if (boost::iequals(mode, "refresh-from-remote") || boost::iequals(mode, "SYNC_REFRESH_FROM_REMOTE")) { + return SYNC_REFRESH_FROM_REMOTE; + } else if (boost::iequals(mode, "refresh-from-local") || boost::iequals(mode, "SYNC_REFRESH_FROM_LOCAL")) { + return SYNC_REFRESH_FROM_LOCAL; + } else if (boost::iequals(mode, "one-way-from-remote") || boost::iequals(mode, "SYNC_ONE_WAY_FROM_REMOTE")) { + return SYNC_ONE_WAY_FROM_REMOTE; + } else if (boost::iequals(mode, "one-way-from-local") || boost::iequals(mode, "SYNC_ONE_WAY_FROM_LOCAL")) { + return SYNC_ONE_WAY_FROM_LOCAL; } else if (boost::iequals(mode, "disabled") || boost::iequals(mode, "SYNC_NONE")) { return SYNC_NONE; } else { diff --git a/src/syncevo/SyncML.h b/src/syncevo/SyncML.h index b61fcce2..2d25eeb8 100644 --- a/src/syncevo/SyncML.h +++ b/src/syncevo/SyncML.h @@ -31,7 +31,43 @@ #include <syncevo/declarations.h> SE_BEGIN_CXX -/** alert Codes used at the synchronization initialization */ +/** + * only the codes which are valid for a server alerted sync (SAN) message + */ +enum SANSyncMode { + SA_SLOW = 201, + SA_FIRST = SA_SLOW, + SA_TWO_WAY = 206, + SA_ONE_WAY_FROM_CLIENT = 207, + SA_REFRESH_FROM_CLIENT = 208, + SA_ONE_WAY_FROM_SERVER = 209, + SA_REFRESH_FROM_SERVER = 210, + SA_LAST = SA_REFRESH_FROM_SERVER, + + SA_INVALID = 255 +}; + +/** + * unambiguous sync modes: + * - data direction is independent of client/server role + * - no server alerted variants + */ +enum SimpleSyncMode { + SIMPLE_SYNC_NONE, + SIMPLE_SYNC_TWO_WAY = 200, + SIMPLE_SYNC_SLOW = 201, + + SIMPLE_SYNC_RESTORE_FROM_BACKUP = 211, + + SIMPLE_SYNC_ONE_WAY_FROM_LOCAL = 212, + SIMPLE_SYNC_REFRESH_FROM_LOCAL = 213, + SIMPLE_SYNC_ONE_WAY_FROM_REMOTE = 214, + SIMPLE_SYNC_REFRESH_FROM_REMOTE = 215, + + SIMPLE_SYNC_INVALID = 255 +}; + +/** all kinds of sync modes */ enum SyncMode { /** unset or disabled */ SYNC_NONE, @@ -44,7 +80,7 @@ enum SyncMode { SYNC_ONE_WAY_FROM_SERVER = 204, SYNC_REFRESH_FROM_SERVER = 205, - /** used by Server Alerted Sync **/ + // used by Server Alerted Sync, same as SA_ in SANSyncMode SA_SYNC_TWO_WAY = 206, SA_SYNC_ONE_WAY_FROM_CLIENT = 207, SA_SYNC_REFRESH_FROM_CLIENT = 208, @@ -54,11 +90,29 @@ enum SyncMode { // used by restore backend with backup data, a pseudo mode SYNC_RESTORE_FROM_BACKUP = 211, + // modes which always transfer in the same direction, + // regardless which side acts as client or server + SYNC_ONE_WAY_FROM_LOCAL = 212, + SYNC_REFRESH_FROM_LOCAL = 213, + SYNC_ONE_WAY_FROM_REMOTE = 214, + SYNC_REFRESH_FROM_REMOTE = 215, + SYNC_LAST = 220, /** error situation (in contrast to SYNC_NONE) */ SYNC_INVALID = 255 }; +/** + * maps to normal SYNC_ variants (no SA_*) and unambiguous direction + * (LOCAL/REMOTE instead of CLIENT/SERVER) + */ +SimpleSyncMode SimplifySyncMode(SyncMode mode, bool peerIsClient); + +/** + * maps to the server alerted variants + */ +SANSyncMode AlertSyncMode(SyncMode mode, bool peerIsClient); + /* According to OMNA WSP Content Type Numbers*/ enum ContentType { WSPCTC_TEXT_PLAIN = 0x03, diff --git a/test/ClientTest.cpp b/test/ClientTest.cpp index 9850ad35..804c7a7b 100644 --- a/test/ClientTest.cpp +++ b/test/ClientTest.cpp @@ -1939,6 +1939,8 @@ void SyncTests::addTests(bool isFirstSource) { ADD_TEST(SyncTests, testSlowSync); ADD_TEST(SyncTests, testRefreshFromServerSync); ADD_TEST(SyncTests, testRefreshFromClientSync); + ADD_TEST(SyncTests, testRefreshFromRemoteSync); + ADD_TEST(SyncTests, testRefreshFromLocalSync); // testTimeout is independent of the actual peer; all it needs // is a SyncML client config. Can't test for that explicitly // here, so only rule out the test if we run in server mode. @@ -2013,6 +2015,8 @@ void SyncTests::addTests(bool isFirstSource) { ADD_TEST(SyncTests, testLargeObject); ADD_TEST(SyncTests, testOneWayFromServer); ADD_TEST(SyncTests, testOneWayFromClient); + ADD_TEST(SyncTests, testOneWayFromRemote); + ADD_TEST(SyncTests, testOneWayFromLocal); } } } @@ -2161,7 +2165,7 @@ void SyncTests::deleteAll(DeleteAllMode mode) { } doSync("refreshserver", SyncOptions(RefreshFromLocalMode(), - CheckSyncReport(0,0,0, 0,0,-1, true, RefreshFromLocalMode()))); + CheckSyncReport(0,0,0, 0,0,-1, true, SYNC_REFRESH_FROM_LOCAL))); break; } } @@ -2250,6 +2254,37 @@ void SyncTests::testDeleteAllRefresh() { } } +// refresh-from-server sync, regardless of peer's role +void SyncTests::testRefreshFromServerSync() +{ + doSync(SyncOptions(SYNC_REFRESH_FROM_SERVER, + CheckSyncReport(-1,-1,-1, -1,-1,-1, true, + isServerMode() ? SYNC_REFRESH_FROM_LOCAL : SYNC_REFRESH_FROM_REMOTE))); +} + +// do a refresh-from-client sync, regardless of peer's role +void SyncTests::testRefreshFromClientSync() +{ + doSync(SyncOptions(SYNC_REFRESH_FROM_CLIENT, + CheckSyncReport(-1,-1,-1, -1,-1,-1, true, + isServerMode() ? SYNC_REFRESH_FROM_REMOTE : SYNC_REFRESH_FROM_LOCAL))); +} + +// do a refresh-from-remote sync, regardless of peer's role +void SyncTests::testRefreshFromRemoteSync() +{ + doSync(SyncOptions(SYNC_REFRESH_FROM_REMOTE, + CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_REMOTE))); +} + +// do a refresh-from-local sync, regardless of peer's role +void SyncTests::testRefreshFromLocalSync() +{ + doSync(SyncOptions(SYNC_REFRESH_FROM_LOCAL, + CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_LOCAL))); +} + + // test that a refresh sync from an empty server leads to an empty datatbase // and no changes are sent to server during next two-way sync void SyncTests::testRefreshFromServerSemantic() { @@ -2264,7 +2299,7 @@ void SyncTests::testRefreshFromServerSemantic() { } doSync("refresh", SyncOptions(RefreshFromPeerMode(), - CheckSyncReport(0,0,-1, 0,0,0, true, RefreshFromPeerMode()))); + CheckSyncReport(0,0,-1, 0,0,0, true, SYNC_REFRESH_FROM_REMOTE))); // check for (it = sources.begin(); it != sources.end(); ++it) { @@ -2302,12 +2337,12 @@ void SyncTests::testRefreshFromClientSemantic() { // refresh from client doSync("refresh", SyncOptions(RefreshFromLocalMode(), - CheckSyncReport(0,0,0, 0,0,0, true, RefreshFromLocalMode()))); + CheckSyncReport(0,0,0, 0,0,0, true, SYNC_REFRESH_FROM_LOCAL))); // check doSync("check", SyncOptions(RefreshFromPeerMode(), - CheckSyncReport(0,0,0, 0,0,0, true, RefreshFromPeerMode()))); + CheckSyncReport(0,0,0, 0,0,0, true, SYNC_REFRESH_FROM_REMOTE))); } // tests the following sequence of events: @@ -2331,7 +2366,7 @@ void SyncTests::testRefreshStatus() { doSync("refresh-from-client", SyncOptions(RefreshFromLocalMode(), CheckSyncReport(0,0,0, -1,-1,-1, /* strictly speaking 1,0,0, but not sure exactly what the server will be told */ - true, RefreshFromLocalMode()))); + true, SYNC_REFRESH_FROM_LOCAL))); doSync("two-way", SyncOptions(SYNC_TWO_WAY, CheckSyncReport(0,0,0, 0,0,0, true, SYNC_TWO_WAY))); @@ -2474,7 +2509,7 @@ void SyncTests::testMerge() { // Be extra careful and pull that data anew and compare once more. doSync("check", SyncOptions(RefreshFromPeerMode(), - CheckSyncReport(-1,-1,-1, -1,-1,-1, true, RefreshFromPeerMode()))); + CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_REMOTE))); CPPUNIT_ASSERT_NO_THROW(compareDatabases()); } @@ -2508,7 +2543,7 @@ void SyncTests::testTwinning() { CPPUNIT_ASSERT_NO_THROW(compareDatabases()); } -// tests one-way sync from server: +// tests one-way sync from peer: // - get both clients and server in sync with no items anywhere // - add one item on first client, copy to server // - add a different item on second client, one-way-from-server @@ -2517,7 +2552,7 @@ void SyncTests::testTwinning() { // - delete on first client, sync that to second client // via two-way sync + one-way-from-server // => one item left on second client (the one inserted locally) -void SyncTests::testOneWayFromServer() { +void SyncTests::doOneWayFromRemote(SyncMode oneWayFromRemote) { // no items anywhere deleteAll(); accessClientB->refreshClient(); @@ -2577,8 +2612,8 @@ void SyncTests::testOneWayFromServer() { } } accessClientB->doSync("recv", - SyncOptions(OneWayFromPeerMode(), - CheckSyncReport(1,0,0, 0,0,0, true, OneWayFromPeerMode()))); + SyncOptions(oneWayFromRemote, + CheckSyncReport(1,0,0, 0,0,0, true, SYNC_ONE_WAY_FROM_REMOTE))); for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) { if (it->second->config.m_createSourceB) { TestingSyncSourcePtr source; @@ -2640,8 +2675,8 @@ void SyncTests::testOneWayFromServer() { // sync the same change to second client // => one item left (the one inserted locally) accessClientB->doSync("delete", - SyncOptions(OneWayFromPeerMode(), - CheckSyncReport(0,0,1, 0,0,0, true, OneWayFromPeerMode()))); + SyncOptions(oneWayFromRemote, + CheckSyncReport(0,0,1, 0,0,0, true, SYNC_ONE_WAY_FROM_REMOTE))); for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) { if (it->second->config.m_createSourceB) { TestingSyncSourcePtr source; @@ -2655,7 +2690,19 @@ void SyncTests::testOneWayFromServer() { } } -// tests one-way sync from client: +// one-way-from-remote test with one-way-from-client/server, depending +// on role of remote side +void SyncTests::testOneWayFromServer() +{ + doOneWayFromRemote(OneWayFromPeerMode()); +} + +void SyncTests::testOneWayFromRemote() +{ + doOneWayFromRemote(SYNC_ONE_WAY_FROM_REMOTE); +} + +// tests one-way sync from local side: // - get both clients and server in sync with no items anywhere // - add one item on first client, copy to server // - add a different item on second client, one-way-from-client @@ -2664,7 +2711,7 @@ void SyncTests::testOneWayFromServer() { // - delete on second client, sync that to first client // via one-way-from-client, two-way // => one item left on first client (the one inserted locally) -void SyncTests::testOneWayFromClient() { +void SyncTests::doOneWayFromLocal(SyncMode oneWayFromLocal) { // no items anywhere deleteAll(); accessClientB->deleteAll(); @@ -2724,8 +2771,8 @@ void SyncTests::testOneWayFromClient() { } } accessClientB->doSync("send", - SyncOptions(OneWayFromLocalMode(), - CheckSyncReport(0,0,0, 1,0,0, true, OneWayFromLocalMode()))); + SyncOptions(oneWayFromLocal, + CheckSyncReport(0,0,0, 1,0,0, true, SYNC_ONE_WAY_FROM_LOCAL))); for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) { if (it->second->config.m_createSourceB) { TestingSyncSourcePtr source; @@ -2770,8 +2817,8 @@ void SyncTests::testOneWayFromClient() { } } accessClientB->doSync("delete", - SyncOptions(OneWayFromLocalMode(), - CheckSyncReport(0,0,0, 0,0,1, true, OneWayFromLocalMode()))); + SyncOptions(oneWayFromLocal, + CheckSyncReport(0,0,0, 0,0,1, true, SYNC_ONE_WAY_FROM_LOCAL))); for (it = accessClientB->sources.begin(); it != accessClientB->sources.end(); ++it) { if (it->second->config.m_createSourceB) { TestingSyncSourcePtr source; @@ -2802,6 +2849,18 @@ void SyncTests::testOneWayFromClient() { } } +// one-way-from-local test with one-way-from-client/server, depending +// on role of local side +void SyncTests::testOneWayFromClient() +{ + doOneWayFromLocal(OneWayFromLocalMode()); +} + +void SyncTests::testOneWayFromLocal() +{ + doOneWayFromLocal(SYNC_ONE_WAY_FROM_LOCAL); +} + // get engine ready, then use it to convert our test items // to and from the internal field list void SyncTests::testConversion() { @@ -3102,7 +3161,7 @@ void SyncTests::testManyDeletes() { accessClientB->doSync("delete-client", SyncOptions(RefreshFromPeerMode(), checkSyncModeStr ? CheckSyncReport() : - CheckSyncReport(0,0,num_items, 0,0,0, true, RefreshFromPeerMode()), + CheckSyncReport(0,0,num_items, 0,0,0, true, SYNC_REFRESH_FROM_REMOTE), 10 * 1024)); } @@ -3136,7 +3195,7 @@ void SyncTests::testSlowSyncSemantic() CheckSyncReport(0,0,0, 0,0,1, true, SYNC_TWO_WAY))); accessClientB->doSync("check", SyncOptions(RefreshFromPeerMode(), - CheckSyncReport(0,0,0, 0,0,0, true, RefreshFromPeerMode()))); + CheckSyncReport(0,0,0, 0,0,0, true, SYNC_REFRESH_FROM_REMOTE))); // now the item should also be deleted on A doSync("delete", @@ -3169,7 +3228,7 @@ void SyncTests::testComplexRefreshFromServerSemantic() accessClientB->doSync("refresh-one", SyncOptions(RefreshFromPeerMode(), checkSyncModeStr ? CheckSyncReport() : - CheckSyncReport(1,0,1, 0,0,0, true, RefreshFromPeerMode()))); + CheckSyncReport(1,0,1, 0,0,0, true, SYNC_REFRESH_FROM_REMOTE))); } // delete that item via A, check again @@ -3185,7 +3244,7 @@ void SyncTests::testComplexRefreshFromServerSemantic() accessClientB->doSync("refresh-none", SyncOptions(RefreshFromPeerMode(), checkSyncModeStr ? CheckSyncReport() : - CheckSyncReport(0,0,1, 0,0,0, true, RefreshFromPeerMode()))); + CheckSyncReport(0,0,1, 0,0,0, true, SYNC_REFRESH_FROM_REMOTE))); } } @@ -3641,7 +3700,7 @@ void SyncTests::doVarSizes(bool withMaxMsgSize, } else { accessClientB->doSync("recv", SyncOptions(RefreshFromPeerMode(), - CheckSyncReport(-1,0,-1, 0,0,0, true, RefreshFromPeerMode()), // number of items received from server depends on source + CheckSyncReport(-1,0,-1, 0,0,0, true, SYNC_REFRESH_FROM_REMOTE), // number of items received from server depends on source withLargeObject ? maxMsgSize : withMaxMsgSize ? maxMsgSize * 100 /* large enough so that server can sent the largest item */ : 0, withMaxMsgSize ? maxMsgSize * 100 : 0, withLargeObject)); diff --git a/test/ClientTest.h b/test/ClientTest.h index 7f09b7b7..b5cd7ca2 100644 --- a/test/ClientTest.h +++ b/test/ClientTest.h @@ -697,17 +697,11 @@ protected: doSync(SyncOptions(SYNC_SLOW, CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_SLOW))); } - // do a refresh from server sync without additional checks - virtual void testRefreshFromServerSync() { - doSync(SyncOptions(SYNC_REFRESH_FROM_SERVER, - CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_SERVER))); - } - // do a refresh from client sync without additional checks - virtual void testRefreshFromClientSync() { - doSync(SyncOptions(SYNC_REFRESH_FROM_CLIENT, - CheckSyncReport(-1,-1,-1, -1,-1,-1, true, SYNC_REFRESH_FROM_CLIENT))); - } + virtual void testRefreshFromServerSync(); + virtual void testRefreshFromClientSync(); + virtual void testRefreshFromRemoteSync(); + virtual void testRefreshFromLocalSync(); // delete all items, locally and on server using two-way sync virtual void testDeleteAllSync() { @@ -730,8 +724,12 @@ protected: virtual void testDelete(); virtual void testMerge(); virtual void testTwinning(); - virtual void testOneWayFromServer(); - virtual void testOneWayFromClient(); + void doOneWayFromRemote(SyncMode oneWayFromRemote); + void testOneWayFromServer(); + void testOneWayFromRemote(); + void doOneWayFromLocal(SyncMode oneWayFromLocal); + void testOneWayFromClient(); + void testOneWayFromLocal(); bool doConversionCallback(bool *success, SyncContext &client, SyncOptions &options); diff --git a/test/test-dbus.py b/test/test-dbus.py index d14c2391..fdf2005f 100755 --- a/test/test-dbus.py +++ b/test/test-dbus.py @@ -1545,9 +1545,10 @@ class TestSessionAPIsDummy(unittest.TestCase, DBusUtil): self.assertEqual(str(ex), "org.syncevolution.InvalidCall: invalid value 'invalid-value' for " "property 'sync': 'not one of the valid values (two-way, slow, " - "refresh-from-client = refresh-client, refresh-from-server = " - "refresh-server = refresh, one-way-from-client = one-way-client, " - "one-way-from-server = one-way-server = one-way, disabled = none)'") + "refresh-from-local, refresh-from-remote = refresh, one-way-from-local, " + "one-way-from-remote = one-way, refresh-from-client = refresh-client, " + "refresh-from-server = refresh-server, one-way-from-client = one-way-client, " + "one-way-from-server = one-way-server, disabled = none)'") else: self.fail("no exception thrown") |