PIM: include CardDAV in CreatePeer()

This adds "protocol: CardDAV" as a valid value, with corresponding
changes to the interpretation of some existing properties and some new
ones. The API itself is not changed.

Suspending a CardDAV sync is possible. This freezes the internal
SyncML message exchange, so data exchange with the CardDAV server may
continue for a while after SuspendPeer().

Photo data is always downloaded immediately. The "pbap-sync" flag
in SyncPeerWithFlags() has no effect.

Syncing can be configured to be one-way (local side is read-only
cache) or two-way (local side is read/write). Meta data must be
written either way, to speed up caching or allow two-way syncing. The
most common case (no changes on either side) will have to be optimized
such that existing meta data is not touched and thus no disk writes
occur.
This commit is contained in:
Patrick Ohly 2014-08-20 17:20:27 +02:00
parent 07292559b7
commit 9581b682a4
3 changed files with 96 additions and 15 deletions

View File

@ -292,21 +292,37 @@ Peers
The following keys are supported for the configuration of a peer:
- "protocol" - defines how to access the address book. "PBAB" (phone
book access protocol) and "file" (read vCard files from
directory) are implemented. "SyncML" and "CardDAV"
book access protocol), "files" (read vCard files from
directory) and "CardDAV" (one-way or two way syncing
with a CardDAV server) are implemented. "SyncML"
could be added easily.
- "transport" - defines how to establish the connection. The only
supported value is "Bluetooth" (for protocol=PBAP or
SyncML), which is also the default if not given
explicitly.
explicitly. Can be left unset for CardDAV.
- "address" - the Bluetooth MAC address in the aa:bb:cc:dd:ee:ff
format (for transport=Bluetooth).
format (for transport=Bluetooth); for CardDAV, this can
be the name of a SyncEvolution configuration template
(for example, "Google") in which case the default
address book on that server will be used unless a
database is set explicitly
- "username", "password" - the credentials to use for CardDAV, can be
left unset for PBAP and file
- "database" - empty or unset for the internal address book
(protocol=PBAP), or the path to the directory
(protocol=file).
(protocol=file), or the URL of a specific contact
collection (protocol=CardDAV, overrides the "address")
- "syncmode" - "cache" for one-way syncing with local read-only data (the only
allowed value for protocol=PBAP and the default if unset),
"two-way" for two-way syncing with locally writable data.
In two-way mode, SyncEvolution minimizes user interaction
when resolving problems (no slow sync prevention, for
example).
- "logdir" - a directory in which directories are created with debug
information about sync session.
@ -322,6 +338,7 @@ The following keys are supported for the configuration of a peer:
Not supported via the API at the moment:
- selecting a specific phone address book
- selecting which vCard properties get cached
- listing all available CardDAV contact collections
Syncing
=======
@ -688,6 +705,14 @@ If matching fails, a contact will get deleted and recreated. The end result
in the unified address book is still the same, because folks does not
rely on the ID for linking.
With CardDAV, only the initial sync downloads all contacts. Any
following sync uses locally stored meta data about the server to
detect changes (added, updated, deleted contacts) and then applies
these changes locally. If an incremental sync is impossible for
whatever reason (for example, the local meta data about the CardDAV
server got lost), SyncEvolution falls back to the PBAP approach of
comparing local and remote data and updating the local side.
Supported fields
----------------

View File

@ -51,6 +51,9 @@ except ImportError:
pass
parser = OptionParser()
parser.add_option("", "--peer",
default=None,
help="Set the peer name. Derived from --bt-mac if unset.")
parser.add_option("-b", "--bt-mac", dest="mac",
default=None,
help="Set the Bluetooth MAC address and thus UID of the phone peer.",
@ -70,6 +73,9 @@ parser.add_option("-m", "--mode",
parser.add_option("", "--sync-flags",
action="store", default='{}',
help="""Additionall SyncPeerWithFlags() flags in JSON notation. For example: '--sync-flags={ "pbap-chunk-transfer-time": 20, "pbap-chunk-time-lambda": 0.5, "pbap-chunk-max-count-photo": 100 }'""")
parser.add_option("", "--peer-config",
action="store", default='{}',
help="""ConfigurePeer() properties in JSON notation. Not needed when --bt-mac is set, the default configuration then will be for PBAP with that phone. Can be used to sync via CardDAV: '--peer-config={"protocol": "CardDAV", "address": "google", "username": "goa:john.doe@gmail.com", "syncmode": "two-way"}'""")
parser.add_option("-f", "--progress-frequency",
action="store", type="float", default=0.0,
help="Override default progress event frequency.")
@ -84,12 +90,13 @@ parser.add_option("-r", "--remove",
help="Remove peer configuration and data.")
(options, args) = parser.parse_args()
if options.configure or options.sync or options.remove:
if not options.mac:
sys.exit('--bt-mac parameter must be given')
peername = options.peer or options.mac
if not peername:
sys.exit('--peer or --bt-mac parameter must be given')
# Use MAC address as UID of peer, but with underscores instead of colons
# Use MAC address as UID of peer, but without colons
# and all in lower case. See https://bugs.freedesktop.org/show_bug.cgi?id=56436
peername = options.mac.replace(':', '').lower()
peername = peername.replace(':', '').lower()
DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
@ -251,8 +258,12 @@ print 'peers: %s' % peers
print 'available databases: %s' % ([''] + ['peer-' + uid for uid in peers.keys()])
if not error and options.configure:
peer = {'protocol': 'PBAP',
'address': options.mac}
peer = json.loads(options.peer_config)
if options.mac:
if not 'protocol' in peer:
peer['protocol'] = 'PBAP'
if not 'address' in peer:
peer['address'] = options.mac
print 'adding peer config %s = %s' % (peername, peer)
manager.SetPeer(peername, peer, **async_args)
run()

View File

@ -1099,14 +1099,21 @@ static const char * const PEER_KEY_PROTOCOL = "protocol";
// static const char * const PEER_SYNCML_PROTOCOL = "SyncML";
static const char * const PEER_PBAP_PROTOCOL = "PBAP";
static const char * const PEER_FILES_PROTOCOL = "files";
static const char * const PEER_CARDDAV_PROTOCOL = "CardDAV";
static const char * const PEER_KEY_TRANSPORT = "transport";
static const char * const PEER_BLUETOOTH_TRANSPORT = "Bluetooth";
// static const char * const PEER_IP_TRANSPORT = "IP";
static const char * const PEER_DEF_TRANSPORT = PEER_BLUETOOTH_TRANSPORT;
static const char * const PEER_KEY_ADDRESS = "address";
static const char * const PEER_KEY_DATABASE = "database";
static const char * const PEER_KEY_USERNAME = "username";
static const char * const PEER_KEY_PASSWORD = "password";
static const char * const PEER_KEY_LOGDIR = "logdir";
static const char * const PEER_KEY_MAXSESSIONS = "maxsessions";
static const char * const PEER_KEY_SYNCMODE = "syncmode";
static const char * const PEER_CACHE_SYNCMODE = "cache";
static const char * const PEER_TWO_WAY_SYNCMODE = "two-way";
static std::string GetEssential(const StringMap &properties, const char *key,
bool allowEmpty = false)
@ -1134,6 +1141,9 @@ void Manager::doSetPeer(const boost::shared_ptr<Session> &session,
std::string database = GetWithDef(properties, PEER_KEY_DATABASE);
std::string logdir = GetWithDef(properties, PEER_KEY_LOGDIR);
std::string maxsessions = GetWithDef(properties, PEER_KEY_MAXSESSIONS);
std::string username = GetWithDef(properties, PEER_KEY_USERNAME);
std::string password = GetWithDef(properties, PEER_KEY_PASSWORD);
std::string syncmode = GetWithDef(properties, PEER_KEY_SYNCMODE);
unsigned maxLogDirs = 0;
if (!maxsessions.empty()) {
// https://svn.boost.org/trac/boost/ticket/5494
@ -1166,6 +1176,7 @@ void Manager::doSetPeer(const boost::shared_ptr<Session> &session,
}
if (protocol == PEER_PBAP_PROTOCOL ||
protocol == PEER_CARDDAV_PROTOCOL ||
protocol == PEER_FILES_PROTOCOL) {
// Create, modify or set local config.
boost::shared_ptr<SyncConfig> config(new SyncConfig(MANAGER_LOCAL_CONFIG + context));
@ -1206,8 +1217,20 @@ void Manager::doSetPeer(const boost::shared_ptr<Session> &session,
boost::shared_ptr<PersistentSyncSourceConfig> source(config->getSyncSourceConfig(MANAGER_LOCAL_SOURCE));
source->setBackend("evolution-contacts");
source->setDatabaseID(localDatabaseName);
source->setSync("local-cache");
source->setURI(MANAGER_REMOTE_SOURCE);
if (protocol == PEER_CARDDAV_PROTOCOL &&
syncmode == PEER_TWO_WAY_SYNCMODE) {
source->setSync("two-way");
} else if (syncmode.empty() ||
syncmode == PEER_CACHE_SYNCMODE) {
source->setSync("local-cache");
} else {
SE_THROW(StringPrintf("peer config: unsupported mode for %s: %s=%s",
protocol.c_str(),
PEER_KEY_SYNCMODE,
syncmode.c_str()));
}
config->flush();
// Ensure that database exists.
SyncSourceParams params(MANAGER_LOCAL_SOURCE,
@ -1241,11 +1264,29 @@ void Manager::doSetPeer(const boost::shared_ptr<Session> &session,
if (!maxsessions.empty()) {
config->setMaxLogDirs(maxLogDirs);
}
if (protocol == PEER_CARDDAV_PROTOCOL) {
if (!address.empty()) {
// Retrieve syncURL from template.
boost::shared_ptr<SyncConfig> peer(SyncConfig::createPeerTemplate(address));
if (!peer) {
SE_THROW(StringPrintf("peer config: no such template: %s=%s",
PEER_KEY_ADDRESS, address.c_str()));
}
config->setSyncURL(peer->getSyncURL());
}
config->setSyncUsername(username);
config->setSyncPassword(password);
}
source = config->getSyncSourceConfig(MANAGER_REMOTE_SOURCE);
if (protocol == PEER_PBAP_PROTOCOL) {
// PBAP
source->setDatabaseID("obex-bt://" + address);
source->setBackend("pbap");
} else if (protocol == PEER_CARDDAV_PROTOCOL) {
// CardDAV
source->setDatabaseID(database);
source->setBackend("carddav");
} else {
// Local sync with files on the target side.
// Format is hard-coded to vCard 3.0.
@ -1549,14 +1590,18 @@ void Manager::doSyncPeer(const boost::shared_ptr<Session> &session,
boost::signals2::at_front);
// Determine sync mode. "pbap" is valid only when the remote
// source uses the PBAP backend. Otherwise we use "ephemeral",
// which ensures that absolutely no sync meta data gets written.
std::string syncMode = "ephemeral";
// source uses the PBAP backend. For the file backend, we use
// "ephemeral", which ensures that absolutely no sync meta data
// gets written (simulates PBAP). Everything else uses normal
// syncing.
std::string syncMode;
std::string context = StringPrintf("@%s%s", MANAGER_PREFIX, uid.c_str());
boost::shared_ptr<SyncConfig> config(new SyncConfig(MANAGER_REMOTE_CONFIG + context));
boost::shared_ptr<PersistentSyncSourceConfig> source(config->getSyncSourceConfig(MANAGER_REMOTE_SOURCE));
if (source->getBackend() == "PBAP Address Book") {
syncMode = "pbap";
} else if (source->getBackend() == "file") {
syncMode = "ephemeral";
}
StringMap env;