summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.rst19
-rw-r--r--src/syncevo/Cmdline.cpp28
-rw-r--r--src/syncevo/LocalTransportAgent.cpp18
-rw-r--r--src/syncevo/SyncConfig.cpp40
-rw-r--r--src/syncevo/SyncConfig.h3
-rw-r--r--src/syncevo/SyncContext.cpp89
-rw-r--r--src/syncevo/SyncML.cpp105
-rw-r--r--src/syncevo/SyncML.h58
-rw-r--r--test/ClientTest.cpp105
-rw-r--r--test/ClientTest.h22
-rwxr-xr-xtest/test-dbus.py7
11 files changed, 370 insertions, 124 deletions
diff --git a/README.rst b/README.rst
index 4a51fe42..16bb95c0 100644
--- a/README.rst
+++ b/README.rst
@@ -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")