implemented front-end based on new SyncML C++ config API
implemented the new begin/endSync() callbacks explain the change tracking and error handling -> copying from server works, the other way around fails, probably because of vcard 3.0 parsing problems on the server git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@6 15ad00c4-1369-45f4-8270-35d70d36bdcd
This commit is contained in:
parent
3a81621f3e
commit
6b09f2f6c3
11 changed files with 473 additions and 127 deletions
48
README
48
README
|
@ -23,20 +23,23 @@ would also be possible, but is not implemented yet. As command line
|
|||
parameters sync4jevolution only supports one option which specifies the
|
||||
configuration file that drive the synchronization run:
|
||||
|
||||
sync4jevolution <server>
|
||||
sync4jevolution [<server>]
|
||||
|
||||
The SyncML <server> has to specified. Selection of sources of
|
||||
The <server> string is used to find the configuration which determines
|
||||
how synchronization is going to proceed. Selection of sources of
|
||||
Evolution data which are to be synchronized with that server is done
|
||||
via configuration files. It is possible to configure sources without
|
||||
activating their synchronization, see the "disabled" property below.
|
||||
|
||||
If the SyncML server is not specified, sync4jevolution lists all
|
||||
available Evolution backend databases.
|
||||
|
||||
Progress and error messages are both sent to stdout. In case of an
|
||||
error the synchronization run is aborted and sync4jevolution returns a
|
||||
non-zero value. If one data source fails, synchronization of the
|
||||
remaining sources is not attempted. Recovery from failed
|
||||
synchronization is done by forcing a full synchronization during the
|
||||
next run, i.e. by sending all items and letting the SyncML server
|
||||
compare against the ones it already knows.
|
||||
non-zero value. Recovery from failed synchronization is done by
|
||||
forcing a full synchronization during the next run, i.e. by sending
|
||||
all items and letting the SyncML server compare against the ones it
|
||||
already knows.
|
||||
|
||||
After a successful synchronization the server's configuration file is
|
||||
updated so that the next run can be done incrementally. If the
|
||||
|
@ -113,8 +116,9 @@ Evolution contacts with the Sync4j server:
|
|||
# text/x-vcard = Evolution contact data
|
||||
type = text/x-vcard
|
||||
|
||||
# picks one of Evolution's data sources, leave empty for default
|
||||
evolutionurl =
|
||||
# picks one of Evolution's data sources:
|
||||
# enter either the name or the full URL
|
||||
evolutionsource =
|
||||
|
||||
# this is appended to the server's URL to identify the
|
||||
# server's database
|
||||
|
@ -139,6 +143,32 @@ If the Evolution data source requires authentication, the
|
|||
this case the directory that contains the source's config.txt should
|
||||
only be accessible by the user.
|
||||
|
||||
Tracking Changes inside Evolution
|
||||
---------------------------------
|
||||
|
||||
The SyncML protocol requires that a client knows which items have been
|
||||
added, modified and deleted since the last sync. This is supported by
|
||||
the Evolution database server, albeit in a limited way:
|
||||
|
||||
The same function lists changes and also moves the so called "change
|
||||
marker" forward. Therefore asking for changes twice in a row will only
|
||||
list changes the first time and not report the same changes a second
|
||||
time. sync4jevolution delays asking for changes as long as possible
|
||||
and only does it when synchronization has really started. Then
|
||||
if synchronization completed and items where added, modified or
|
||||
deleted on behalf of the server, the change marker is moved forward.
|
||||
|
||||
If synchronization fails for some or all items, then sync4jevolution
|
||||
cannot mark individual items for retransmission during the next
|
||||
sync and forces the next sync to execute in slow mode.
|
||||
|
||||
The change marker that sync4jevolution uses is a string which is
|
||||
composed as "sync4jevolution:<syncURL>/<name>" where <syncURL> comes
|
||||
from the server config file and <name> from the source config
|
||||
file. This implies that changes are tracked separately for each server
|
||||
and server database that Evolution might be synchronized with.
|
||||
|
||||
|
||||
Compiling from Source
|
||||
---------------------
|
||||
|
||||
|
|
|
@ -21,22 +21,12 @@ using namespace std;
|
|||
|
||||
#include "EvolutionContactSource.h"
|
||||
|
||||
class EvolutionContactItem : public SyncItem
|
||||
{
|
||||
public:
|
||||
EvolutionContactItem( EContact *contact );
|
||||
};
|
||||
|
||||
EvolutionContactItem::EvolutionContactItem( EContact *contact )
|
||||
{
|
||||
}
|
||||
|
||||
EvolutionContactSource::EvolutionContactSource( const string &name,
|
||||
const string &changeId,
|
||||
const string &id,
|
||||
bool idIsName ) :
|
||||
EvolutionSyncSource( name, changeId, id, idIsName ),
|
||||
m_vcardFormat( EVC_FORMAT_VCARD_30 )
|
||||
EVCardFormat vcardFormat ) :
|
||||
EvolutionSyncSource( name, changeId, id ),
|
||||
m_vcardFormat( vcardFormat )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -73,9 +63,9 @@ void EvolutionContactSource::open()
|
|||
throw "unable to access address books";
|
||||
}
|
||||
|
||||
ESource *source = findSource( sources, m_id, m_idIsName );
|
||||
ESource *source = findSource( sources, m_id );
|
||||
if (!source) {
|
||||
throw string( "no such address book: " ) + m_id;
|
||||
throw string(getName()) + ": no such address book: '" + m_id + "'";
|
||||
}
|
||||
|
||||
GError *gerror = NULL;
|
||||
|
@ -84,46 +74,70 @@ void EvolutionContactSource::open()
|
|||
if (!e_book_open( m_addressbook, TRUE, &gerror) ) {
|
||||
throwError( "opening address book", gerror );
|
||||
}
|
||||
|
||||
// find all items
|
||||
gptr<EBookQuery> allItemsQuery( e_book_query_any_field_contains(""), "query" );
|
||||
GList *nextItem;
|
||||
if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) {
|
||||
throwError( "reading all items", gerror );
|
||||
}
|
||||
while (nextItem) {
|
||||
m_allItems.push_back( (const char *)e_contact_get_const(E_CONTACT(nextItem->data),
|
||||
E_CONTACT_UID) );
|
||||
nextItem = nextItem->next;
|
||||
}
|
||||
allItemsQuery = NULL;
|
||||
|
||||
// scan modified items since the last instantiation
|
||||
if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) {
|
||||
throwError( "reading changes", gerror );
|
||||
}
|
||||
while (nextItem) {
|
||||
EBookChange *ebc = (EBookChange *)nextItem->data;
|
||||
const char *uid = (const char *)e_contact_get_const( ebc->contact, E_CONTACT_UID );
|
||||
|
||||
switch (ebc->change_type) {
|
||||
case E_BOOK_CHANGE_CARD_ADDED:
|
||||
m_newItems.push_back( uid );
|
||||
break;
|
||||
case E_BOOK_CHANGE_CARD_MODIFIED:
|
||||
m_updatedItems.push_back( uid );
|
||||
break;
|
||||
case E_BOOK_CHANGE_CARD_DELETED:
|
||||
m_deletedItems.push_back( uid );
|
||||
break;
|
||||
}
|
||||
nextItem = nextItem->next;
|
||||
}
|
||||
}
|
||||
|
||||
void EvolutionContactSource::close()
|
||||
int EvolutionContactSource::beginSync()
|
||||
{
|
||||
if (m_addressbook) {
|
||||
m_isModified = false;
|
||||
try {
|
||||
GError *gerror = NULL;
|
||||
|
||||
// find all items
|
||||
gptr<EBookQuery> allItemsQuery( e_book_query_any_field_contains(""), "query" );
|
||||
GList *nextItem;
|
||||
if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) {
|
||||
throwError( "reading all items", gerror );
|
||||
}
|
||||
while (nextItem) {
|
||||
m_allItems.push_back( (const char *)e_contact_get_const(E_CONTACT(nextItem->data),
|
||||
E_CONTACT_UID) );
|
||||
nextItem = nextItem->next;
|
||||
}
|
||||
allItemsQuery = NULL;
|
||||
|
||||
// scan modified items since the last instantiation
|
||||
if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) {
|
||||
throwError( "reading changes", gerror );
|
||||
}
|
||||
while (nextItem) {
|
||||
EBookChange *ebc = (EBookChange *)nextItem->data;
|
||||
const char *uid = (const char *)e_contact_get_const( ebc->contact, E_CONTACT_UID );
|
||||
|
||||
switch (ebc->change_type) {
|
||||
case E_BOOK_CHANGE_CARD_ADDED:
|
||||
m_newItems.push_back( uid );
|
||||
break;
|
||||
case E_BOOK_CHANGE_CARD_MODIFIED:
|
||||
m_updatedItems.push_back( uid );
|
||||
break;
|
||||
case E_BOOK_CHANGE_CARD_DELETED:
|
||||
m_deletedItems.push_back( uid );
|
||||
break;
|
||||
}
|
||||
nextItem = nextItem->next;
|
||||
}
|
||||
} catch( ... ) {
|
||||
m_hasFailed = true;
|
||||
// TODO: properly set error
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EvolutionContactSource::endSync()
|
||||
{
|
||||
try {
|
||||
endSyncThrow();
|
||||
} catch ( ... ) {
|
||||
m_hasFailed = true;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EvolutionContactSource::endSyncThrow()
|
||||
{
|
||||
if (m_isModified) {
|
||||
GError *gerror = NULL;
|
||||
GList *nextItem;
|
||||
// move change_id forward so that our own changes are not listed the next time
|
||||
|
@ -131,11 +145,14 @@ void EvolutionContactSource::close()
|
|||
throwError( "reading changes", gerror );
|
||||
}
|
||||
}
|
||||
resetItems();
|
||||
m_isModified = false;
|
||||
}
|
||||
|
||||
void EvolutionContactSource::close()
|
||||
{
|
||||
endSyncThrow();
|
||||
m_addressbook = NULL;
|
||||
m_allItems.clear();
|
||||
m_newItems.clear();
|
||||
m_updatedItems.clear();
|
||||
m_deletedItems.clear();
|
||||
}
|
||||
|
||||
|
||||
|
@ -192,10 +209,13 @@ int EvolutionContactSource::addItem(SyncItem& item)
|
|||
throwError( string( "parsing vcard" ) + data,
|
||||
NULL );
|
||||
}
|
||||
|
||||
m_isModified = true;
|
||||
} catch ( ... ) {
|
||||
m_hasFailed = true;
|
||||
return STC_COMMAND_FAILED;
|
||||
}
|
||||
return 0;
|
||||
return STC_OK;
|
||||
}
|
||||
|
||||
int EvolutionContactSource::updateItem(SyncItem& item)
|
||||
|
@ -218,10 +238,13 @@ int EvolutionContactSource::updateItem(SyncItem& item)
|
|||
throwError( string( "parsing vcard" ) + data,
|
||||
NULL );
|
||||
}
|
||||
|
||||
m_isModified = true;
|
||||
} catch ( ... ) {
|
||||
m_hasFailed = true;
|
||||
return STC_COMMAND_FAILED;
|
||||
}
|
||||
return 0;
|
||||
return STC_OK;
|
||||
}
|
||||
|
||||
int EvolutionContactSource::deleteItem(SyncItem& item)
|
||||
|
@ -232,10 +255,13 @@ int EvolutionContactSource::deleteItem(SyncItem& item)
|
|||
throwError( string( "deleting contact" ) + item.getKey(),
|
||||
gerror );
|
||||
}
|
||||
|
||||
m_isModified = true;
|
||||
} catch( ... ) {
|
||||
m_hasFailed = true;
|
||||
return STC_COMMAND_FAILED;
|
||||
}
|
||||
return 0;
|
||||
return STC_OK;
|
||||
}
|
||||
|
||||
const char *EvolutionContactSource::getMimeType()
|
||||
|
|
|
@ -30,16 +30,16 @@ class EvolutionContactSource : public EvolutionSyncSource
|
|||
public:
|
||||
/**
|
||||
* Creates a new Evolution address book source.
|
||||
*
|
||||
* @param changeId is used to track changes in the Evolution backend;
|
||||
* not specifying it implies that always all items are returned
|
||||
* @param id identifies the backend; not specifying it makes this instance
|
||||
* unusable for anything but listing backend databases
|
||||
* @param idIsName true iff id references to the database name, otherwise its uri
|
||||
*/
|
||||
EvolutionContactSource( const string &name,
|
||||
const string &changeId = string(""),
|
||||
const string &id = string(""),
|
||||
bool idIsName = false );
|
||||
EVCardFormat vcardFormat = EVC_FORMAT_VCARD_30 );
|
||||
virtual ~EvolutionContactSource();
|
||||
|
||||
//
|
||||
|
@ -56,7 +56,9 @@ class EvolutionContactSource : public EvolutionSyncSource
|
|||
virtual int addItem(SyncItem& item);
|
||||
virtual int updateItem(SyncItem& item);
|
||||
virtual int deleteItem(SyncItem& item);
|
||||
|
||||
virtual int beginSync();
|
||||
virtual int endSync();
|
||||
|
||||
private:
|
||||
/** valid after open(): the address book that this source references */
|
||||
gptr<EBook, GObject> m_addressbook;
|
||||
|
@ -66,4 +68,7 @@ class EvolutionContactSource : public EvolutionSyncSource
|
|||
|
||||
/** the mime type which corresponds to m_vcardFormat */
|
||||
const char *getMimeType();
|
||||
|
||||
/** internal implementation of endSync() which will throw an exception in case of failure */
|
||||
void endSyncThrow();
|
||||
};
|
||||
|
|
128
src/EvolutionSyncClient.cpp
Normal file
128
src/EvolutionSyncClient.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Patrick Ohly
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "EvolutionSyncClient.h"
|
||||
#include "EvolutionSyncSource.h"
|
||||
|
||||
#include <spdm/DMTree.h>
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
EvolutionSyncClient::EvolutionSyncClient(const string &server) :
|
||||
m_client(Sync4jClient::getInstance()),
|
||||
m_server(server),
|
||||
m_configPath(string("evolution/") + server)
|
||||
{
|
||||
m_client.setDMConfig(m_configPath.c_str());
|
||||
}
|
||||
|
||||
EvolutionSyncClient::~EvolutionSyncClient()
|
||||
{
|
||||
Sync4jClient::dispose();
|
||||
}
|
||||
|
||||
void EvolutionSyncClient::sync()
|
||||
{
|
||||
class sourcelist : public list<EvolutionSyncSource *> {
|
||||
public:
|
||||
~sourcelist() {
|
||||
for( iterator it = begin();
|
||||
it != end();
|
||||
++it ) {
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
} sources;
|
||||
DMTree config(m_configPath.c_str());
|
||||
|
||||
// find server URL (part of change id)
|
||||
string serverPath = m_configPath + "/spds/syncml";
|
||||
auto_ptr<ManagementNode> serverNode(config.getManagementNode(serverPath.c_str()));
|
||||
string url = EvolutionSyncSource::getPropertyValue(*serverNode, "syncURL");
|
||||
|
||||
// find sources
|
||||
string sourcesPath = m_configPath + "/spds/sources";
|
||||
auto_ptr<ManagementNode> sourcesNode(config.getManagementNode(sourcesPath.c_str()));
|
||||
int index, numSources = sourcesNode->getChildrenMaxCount();
|
||||
char **sourceNamesPtr = sourcesNode->getChildrenNames();
|
||||
|
||||
// copy source names into format that will be
|
||||
// freed in case of exception
|
||||
vector<string> sourceNames;
|
||||
for ( index = 0; index < numSources; index++ ) {
|
||||
sourceNames.push_back(sourceNamesPtr[index]);
|
||||
delete [] sourceNamesPtr[index];
|
||||
}
|
||||
delete [] sourceNamesPtr;
|
||||
|
||||
// iterate over sources
|
||||
for ( index = 0; index < numSources; index++ ) {
|
||||
// is the source enabled?
|
||||
string sourcePath(sourcesPath + "/" + sourceNames[index]);
|
||||
auto_ptr<ManagementNode> sourceNode(config.getManagementNode(sourcePath.c_str()));
|
||||
string disabled = EvolutionSyncSource::getPropertyValue(*sourceNode, "disabled");
|
||||
if (disabled != "T" && disabled != "t") {
|
||||
// create it
|
||||
string type = EvolutionSyncSource::getPropertyValue(*sourceNode, "type");
|
||||
EvolutionSyncSource *syncSource =
|
||||
EvolutionSyncSource::createSource(
|
||||
sourceNames[index],
|
||||
string("sync4jevolution:") + url + "/" + EvolutionSyncSource::getPropertyValue(*sourceNode, "name"),
|
||||
EvolutionSyncSource::getPropertyValue(*sourceNode, "evolutionsource"),
|
||||
type
|
||||
);
|
||||
if (!syncSource) {
|
||||
throw sourceNames[index] + ": type " +
|
||||
( type.size() ? string("not configured") :
|
||||
string("'") + type + "' empty or unknown" );
|
||||
}
|
||||
sources.push_back(syncSource);
|
||||
|
||||
// also open it; failing now is still safe
|
||||
syncSource->open();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sources.size()) {
|
||||
LOG.info( "no sources configured, done" );
|
||||
return;
|
||||
}
|
||||
|
||||
// build array as sync wants it, then sync
|
||||
// (no exceptions allowed here)
|
||||
SyncSource **sourceArray = new SyncSource *[sources.size() + 1];
|
||||
index = 0;
|
||||
for ( list<EvolutionSyncSource *>::iterator it = sources.begin();
|
||||
it != sources.end();
|
||||
++it ) {
|
||||
sourceArray[index] = *it;
|
||||
++index;
|
||||
}
|
||||
sourceArray[index] = NULL;
|
||||
int res = m_client.sync( sourceArray );
|
||||
delete [] sourceArray;
|
||||
|
||||
// TODO: force slow sync in case of res != STC_OK or failed Evolution source
|
||||
|
||||
if (res != STC_OK) {
|
||||
throw lastErrorCode;
|
||||
}
|
||||
}
|
53
src/EvolutionSyncClient.h
Normal file
53
src/EvolutionSyncClient.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2005 Patrick Ohly
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef INCL_EVOLUTIONSYNCCLIENT
|
||||
#define INCL_EVOLUTIONSYNCCLIENT
|
||||
|
||||
#include <common/client/Sync4jClient.h>
|
||||
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
* This is the main class inside sync4jevolution which
|
||||
* looks at the configuration, activates all enabled
|
||||
* sources and executes the synchronization.
|
||||
*
|
||||
* Despite the name it is not a Sync4jClient, but rather
|
||||
* uses one.
|
||||
*/
|
||||
class EvolutionSyncClient {
|
||||
Sync4jClient& m_client;
|
||||
const string m_server;
|
||||
const string m_configPath;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param server identifies the server config to be used
|
||||
*/
|
||||
EvolutionSyncClient(const string &server);
|
||||
~EvolutionSyncClient();
|
||||
|
||||
/**
|
||||
* executes the sync, throws an exception in case of failure
|
||||
*/
|
||||
void sync();
|
||||
};
|
||||
|
||||
#endif // INCL_EVOLUTIONSYNCCLIENT
|
|
@ -17,17 +17,19 @@
|
|||
*/
|
||||
|
||||
#include "EvolutionSyncSource.h"
|
||||
#include "EvolutionContactSource.h"
|
||||
|
||||
ESource *EvolutionSyncSource::findSource( ESourceList *list, const string &id, bool isName )
|
||||
#include <common/base/Log.h>
|
||||
|
||||
ESource *EvolutionSyncSource::findSource( ESourceList *list, const string &id )
|
||||
{
|
||||
for (GSList *g = e_source_list_peek_groups (list); g; g = g->next) {
|
||||
ESourceGroup *group = E_SOURCE_GROUP (g->data);
|
||||
GSList *s;
|
||||
for (s = e_source_group_peek_sources (group); s; s = s->next) {
|
||||
ESource *source = E_SOURCE (s->data);
|
||||
if ( !id.compare( isName ?
|
||||
e_source_peek_name(source) :
|
||||
e_source_get_uri(source) ) )
|
||||
if ( !id.compare(e_source_peek_name(source)) ||
|
||||
!id.compare(e_source_get_uri(source)) )
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +46,18 @@ void EvolutionSyncSource::throwError( const string &action, GError *gerror )
|
|||
} else {
|
||||
gerrorstr = ": failed";
|
||||
}
|
||||
|
||||
throw m_id + ": " + action + gerrorstr;
|
||||
|
||||
string error = string(getName()) + ": " + action + gerrorstr;
|
||||
LOG.error( error.c_str() );
|
||||
throw error;
|
||||
}
|
||||
|
||||
void EvolutionSyncSource::resetItems()
|
||||
{
|
||||
m_allItems.clear();
|
||||
m_newItems.clear();
|
||||
m_updatedItems.clear();
|
||||
m_deletedItems.clear();
|
||||
}
|
||||
|
||||
string EvolutionSyncSource::getData(SyncItem& item)
|
||||
|
@ -58,3 +70,44 @@ string EvolutionSyncSource::getData(SyncItem& item)
|
|||
free(mem);
|
||||
return res;
|
||||
}
|
||||
|
||||
string EvolutionSyncSource::getPropertyValue(ManagementNode &node, const string &property)
|
||||
{
|
||||
char *value = node.getPropertyValue(property.c_str());
|
||||
string res;
|
||||
|
||||
if (value) {
|
||||
res = value;
|
||||
delete [] value;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
EvolutionSyncSource *EvolutionSyncSource::createSource(
|
||||
const string &name,
|
||||
const string &changeId,
|
||||
const string &id,
|
||||
const string &mimeType
|
||||
)
|
||||
{
|
||||
if (mimeType == "text/x-vcard") {
|
||||
return new EvolutionContactSource(name, changeId, id, EVC_FORMAT_VCARD_30);
|
||||
}
|
||||
|
||||
// TODO: other mime types?
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void EvolutionSyncSource::setItemStatus(const char *key, int status)
|
||||
{
|
||||
if (status < 200 || status > 300) {
|
||||
char buffer[200];
|
||||
|
||||
sprintf(buffer,
|
||||
"unexpected SyncML status response %d for item %.80s\n",
|
||||
status, key);
|
||||
LOG.error(buffer);
|
||||
m_hasFailed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef INCL_EVOLUTIONSYNCSOURCE
|
||||
#define INCL_EVOLUTIONSYNCSOURCE
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
@ -24,7 +27,8 @@ using namespace std;
|
|||
#include <libedataserver/e-source.h>
|
||||
#include <libedataserver/e-source-list.h>
|
||||
|
||||
#include <common/spds/SyncSource.h>
|
||||
#include <spds/SyncSource.h>
|
||||
#include <spdm/ManagementNode.h>
|
||||
|
||||
/**
|
||||
* This class implements the functionality shared by
|
||||
|
@ -57,9 +61,8 @@ class EvolutionSyncSource : public SyncSource
|
|||
* @param changeId is used to track changes in the Evolution backend
|
||||
* @param id identifies the backend; not specifying it makes this instance
|
||||
* unusable for anything but listing backend databases
|
||||
* @param idIsName true iff id references to the database name, otherwise its uri
|
||||
*/
|
||||
EvolutionSyncSource( const string name, const string &changeId, const string &id, bool idIsName ) :
|
||||
EvolutionSyncSource( const string name, const string &changeId, const string &id ) :
|
||||
SyncSource( name.c_str() ),
|
||||
m_allItems( *this, SYNC_STATE_NONE ),
|
||||
m_newItems( *this, SYNC_STATE_NEW ),
|
||||
|
@ -67,8 +70,8 @@ class EvolutionSyncSource : public SyncSource
|
|||
m_deletedItems( *this, SYNC_STATE_DELETED ),
|
||||
m_changeId( changeId ),
|
||||
m_hasFailed( false ),
|
||||
m_id( id ),
|
||||
m_idIsName( idIsName )
|
||||
m_isModified( false ),
|
||||
m_id( id )
|
||||
{}
|
||||
virtual ~EvolutionSyncSource() {}
|
||||
|
||||
|
@ -87,36 +90,13 @@ class EvolutionSyncSource : public SyncSource
|
|||
virtual sources getSyncBackends() = 0;
|
||||
|
||||
/**
|
||||
* actually opens the data source specified in the constructor,
|
||||
* will throw the normal exceptions if that fails
|
||||
* Actually opens the data source specified in the constructor,
|
||||
* will throw the normal exceptions if that fails. Should
|
||||
* not modify the state of the sync source: that can be deferred
|
||||
* until the server is also ready and beginSync() is called.
|
||||
*/
|
||||
virtual void open() = 0;
|
||||
|
||||
/**
|
||||
* closes the data source so that it can be reopened
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
/**
|
||||
* returns true iff some failure occured
|
||||
*/
|
||||
bool hasFailed() { return m_hasFailed; }
|
||||
|
||||
//
|
||||
// default implementation of SyncSource iterators
|
||||
//
|
||||
virtual int beginSync() { return STC_OK; }
|
||||
virtual SyncItem* getFirstItem() { return m_allItems.start(); }
|
||||
virtual SyncItem* getNextItem() { return m_allItems.iterate(); }
|
||||
virtual SyncItem* getFirstNewItem() { return m_newItems.start(); }
|
||||
virtual SyncItem* getNextNewItem() { return m_newItems.iterate(); }
|
||||
virtual SyncItem* getFirstUpdatedItem() { return m_updatedItems.start(); }
|
||||
virtual SyncItem* getNextUpdatedItem() { return m_updatedItems.iterate(); }
|
||||
virtual SyncItem* getFirstDeletedItem() { return m_deletedItems.start(); }
|
||||
virtual SyncItem* getNextDeletedItem() { return m_deletedItems.iterate(); }
|
||||
virtual int endSync() { return STC_OK; }
|
||||
virtual void setItemStatus(const char *key, int status) {};
|
||||
|
||||
/**
|
||||
* Extract information for the item identified by UID
|
||||
* and store it in a new SyncItem. The caller must
|
||||
|
@ -127,19 +107,69 @@ class EvolutionSyncSource : public SyncSource
|
|||
*/
|
||||
virtual SyncItem *createItem( const string &uid, SyncState state ) = 0;
|
||||
|
||||
/**
|
||||
* closes the data source so that it can be reopened
|
||||
*
|
||||
* Just as open() it should not affect the state of
|
||||
* the database unless some previous action requires
|
||||
* it.
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
/**
|
||||
* resets the lists of all/new/updated/deleted items
|
||||
*/
|
||||
void resetItems();
|
||||
|
||||
/**
|
||||
* returns true iff some failure occured
|
||||
*/
|
||||
bool hasFailed() { return m_hasFailed; }
|
||||
|
||||
/** convenience function: copies item's data into string */
|
||||
static string getData(SyncItem& item);
|
||||
|
||||
/**
|
||||
* convenience function: gets property as string class
|
||||
*
|
||||
* @return empty string if property not found, otherwise its value
|
||||
*/
|
||||
static string getPropertyValue(ManagementNode &node, const string &property);
|
||||
|
||||
/**
|
||||
* factory function for a EvolutionSyncSources that provides the
|
||||
* given mime type; for the other parameters see constructor
|
||||
*
|
||||
* @return NULL if no source can handle the given type
|
||||
*/
|
||||
static EvolutionSyncSource *createSource(
|
||||
const string &name,
|
||||
const string &changeId,
|
||||
const string &id,
|
||||
const string &mimeType );
|
||||
|
||||
//
|
||||
// default implementation of SyncSource iterators
|
||||
//
|
||||
virtual SyncItem* getFirstItem() { return m_allItems.start(); }
|
||||
virtual SyncItem* getNextItem() { return m_allItems.iterate(); }
|
||||
virtual SyncItem* getFirstNewItem() { return m_newItems.start(); }
|
||||
virtual SyncItem* getNextNewItem() { return m_newItems.iterate(); }
|
||||
virtual SyncItem* getFirstUpdatedItem() { return m_updatedItems.start(); }
|
||||
virtual SyncItem* getNextUpdatedItem() { return m_updatedItems.iterate(); }
|
||||
virtual SyncItem* getFirstDeletedItem() { return m_deletedItems.start(); }
|
||||
virtual SyncItem* getNextDeletedItem() { return m_deletedItems.iterate(); }
|
||||
virtual void setItemStatus(const char *key, int status);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* searches the list for a source with the given uri or name
|
||||
*
|
||||
* @param list a list previously obtained from Gnome
|
||||
* @param id a string identifying the data source: either its name or uri
|
||||
* @param isName id is the name, otherwise uri
|
||||
* @return pointer to source or NULL if not found
|
||||
*/
|
||||
ESource *findSource( ESourceList *list, const string &id, bool isName );
|
||||
ESource *findSource( ESourceList *list, const string &id );
|
||||
|
||||
/**
|
||||
* throw an exception after a Gnome action failed and
|
||||
|
@ -153,7 +183,6 @@ class EvolutionSyncSource : public SyncSource
|
|||
|
||||
const string m_changeId;
|
||||
const string m_id;
|
||||
const bool m_idIsName;
|
||||
|
||||
class itemList : public list<string> {
|
||||
const_iterator m_it;
|
||||
|
@ -196,5 +225,9 @@ class EvolutionSyncSource : public SyncSource
|
|||
|
||||
/** keeps track of failure state */
|
||||
bool m_hasFailed;
|
||||
|
||||
/** remembers whether items have been modified during the sync */
|
||||
bool m_isModified;
|
||||
};
|
||||
|
||||
#endif // INCL_EVOLUTIONSYNCSOURCE
|
||||
|
|
|
@ -7,6 +7,7 @@ bin_PROGRAMS = sync4jevolution
|
|||
|
||||
CORE_SOURCES = \
|
||||
EvolutionSyncSource.cpp \
|
||||
EvolutionSyncClient.cpp \
|
||||
EvolutionContactSource.cpp
|
||||
CORE_LDADD = @EPACKAGE_LIBS@ @SYNC4J_LIBS@ @LIBS@
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ bin_PROGRAMS = sync4jevolution
|
|||
|
||||
CORE_SOURCES = \
|
||||
EvolutionSyncSource.cpp \
|
||||
EvolutionSyncClient.cpp \
|
||||
EvolutionContactSource.cpp
|
||||
|
||||
CORE_LDADD = @EPACKAGE_LIBS@ @SYNC4J_LIBS@ @LIBS@
|
||||
|
@ -177,7 +178,7 @@ check_PROGRAMS = test$(EXEEXT)
|
|||
PROGRAMS = $(bin_PROGRAMS)
|
||||
|
||||
am__objects_1 = EvolutionSyncSource.$(OBJEXT) \
|
||||
EvolutionContactSource.$(OBJEXT)
|
||||
EvolutionSyncClient.$(OBJEXT) EvolutionContactSource.$(OBJEXT)
|
||||
am_sync4jevolution_OBJECTS = sync4jevolution.$(OBJEXT) $(am__objects_1)
|
||||
sync4jevolution_OBJECTS = $(am_sync4jevolution_OBJECTS)
|
||||
sync4jevolution_DEPENDENCIES =
|
||||
|
@ -192,6 +193,7 @@ DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
|
|||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/EvolutionContactSource.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/EvolutionSyncClient.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/EvolutionSyncSource.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/TestEvolution.Po \
|
||||
@AMDEP_TRUE@ ./$(DEPDIR)/TestMain.Po \
|
||||
|
@ -267,6 +269,7 @@ distclean-compile:
|
|||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EvolutionContactSource.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EvolutionSyncClient.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EvolutionSyncSource.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TestEvolution.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TestMain.Po@am__quote@
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
|
||||
#include <EvolutionContactSource.h>
|
||||
#include <common/spds/SyncStatus.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
@ -140,8 +141,7 @@ void TestEvolution::testContactOpen()
|
|||
{
|
||||
EvolutionContactSource source( string( "dummy" ),
|
||||
m_changeIds[0],
|
||||
m_contactName,
|
||||
true );
|
||||
m_contactName );
|
||||
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
}
|
||||
|
@ -173,10 +173,10 @@ void TestEvolution::testContactSimpleInsert()
|
|||
EvolutionContactSource source(
|
||||
string( "dummy" ),
|
||||
m_changeIds[0],
|
||||
m_contactName,
|
||||
true );
|
||||
m_contactName );
|
||||
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
int numItems;
|
||||
CPPUNIT_ASSERT_NO_THROW( numItems = countItems( source ) );
|
||||
SyncItem item;
|
||||
|
@ -187,6 +187,7 @@ void TestEvolution::testContactSimpleInsert()
|
|||
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.close() );
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
CPPUNIT_ASSERT( countItems( source ) == numItems + 1 );
|
||||
CPPUNIT_ASSERT( countNewItems( source ) == 0 );
|
||||
CPPUNIT_ASSERT( countUpdatedItems( source ) == 0 );
|
||||
|
@ -206,10 +207,10 @@ void TestEvolution::testContactDeleteAll()
|
|||
|
||||
EvolutionContactSource source( string( "dummy" ),
|
||||
m_changeIds[0],
|
||||
m_contactName,
|
||||
true );
|
||||
m_contactName );
|
||||
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
int numItems = countItems( source );
|
||||
CPPUNIT_ASSERT( numItems > 0 );
|
||||
|
||||
|
@ -223,6 +224,7 @@ void TestEvolution::testContactDeleteAll()
|
|||
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.close() );
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
EVOLUTION_ASSERT_MESSAGE(
|
||||
"should be empty now",
|
||||
source,
|
||||
|
@ -236,10 +238,10 @@ void TestEvolution::testContactIterateTwice()
|
|||
{
|
||||
EvolutionContactSource source( string( "dummy" ),
|
||||
m_changeIds[0],
|
||||
m_contactName,
|
||||
true );
|
||||
m_contactName );
|
||||
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
EVOLUTION_ASSERT_MESSAGE(
|
||||
"iterating twice should produce identical results",
|
||||
source,
|
||||
|
@ -257,9 +259,9 @@ void TestEvolution::contactUpdate()
|
|||
{
|
||||
EvolutionContactSource source( string( "dummy" ),
|
||||
m_changeIds[0],
|
||||
m_contactName,
|
||||
true );
|
||||
m_contactName );
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
SyncItem *item;
|
||||
EVOLUTION_ASSERT_NO_THROW( source, item = source.getFirstItem() );
|
||||
const char *vcard =
|
||||
|
@ -288,6 +290,7 @@ void TestEvolution::contactUpdate()
|
|||
EVOLUTION_ASSERT_NO_THROW( source, source.close() );
|
||||
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
CPPUNIT_ASSERT( countItems( source ) == 1 );
|
||||
CPPUNIT_ASSERT( countNewItems( source ) == 0 );
|
||||
CPPUNIT_ASSERT( countUpdatedItems( source ) == 0 );
|
||||
|
@ -315,15 +318,16 @@ void TestEvolution::testContactChanges()
|
|||
|
||||
EvolutionContactSource source( string( "dummy" ),
|
||||
m_changeIds[1],
|
||||
m_contactName,
|
||||
true );
|
||||
m_contactName );
|
||||
|
||||
// update change id #1
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.close() );
|
||||
|
||||
// no new changes
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
CPPUNIT_ASSERT( countItems( source ) == 1 );
|
||||
CPPUNIT_ASSERT( countNewItems( source ) == 0 );
|
||||
CPPUNIT_ASSERT( countUpdatedItems( source ) == 0 );
|
||||
|
@ -335,6 +339,7 @@ void TestEvolution::testContactChanges()
|
|||
// delete item again
|
||||
testContactDeleteAll();
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
CPPUNIT_ASSERT( countItems( source ) == 0 );
|
||||
CPPUNIT_ASSERT( countNewItems( source ) == 0 );
|
||||
CPPUNIT_ASSERT( countUpdatedItems( source ) == 0 );
|
||||
|
@ -352,6 +357,7 @@ void TestEvolution::testContactChanges()
|
|||
// insert another item
|
||||
testContactSimpleInsert();
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
CPPUNIT_ASSERT( countItems( source ) == 1 );
|
||||
CPPUNIT_ASSERT( countNewItems( source ) == 1 );
|
||||
CPPUNIT_ASSERT( countUpdatedItems( source ) == 0 );
|
||||
|
@ -369,6 +375,7 @@ void TestEvolution::testContactChanges()
|
|||
// update item
|
||||
contactUpdate();
|
||||
EVOLUTION_ASSERT_NO_THROW( source, source.open() );
|
||||
EVOLUTION_ASSERT( source, source.beginSync() == 0 );
|
||||
CPPUNIT_ASSERT( countItems( source ) == 1 );
|
||||
CPPUNIT_ASSERT( countNewItems( source ) == 0 );
|
||||
CPPUNIT_ASSERT( countUpdatedItems( source ) == 1 );
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
using namespace std;
|
||||
|
||||
#include "EvolutionContactSource.h"
|
||||
#include "EvolutionSyncClient.h"
|
||||
|
||||
/**
|
||||
* list all known data sources of a certain type
|
||||
|
@ -53,8 +54,12 @@ int main( int argc, char **argv )
|
|||
listSources( contactSource, "address books" );
|
||||
|
||||
fprintf( stderr, "usage: %s <server>\n", argv[0] );
|
||||
return 1;
|
||||
} else {
|
||||
EvolutionSyncClient client(argv[1]);
|
||||
client.sync();
|
||||
}
|
||||
|
||||
return 0;
|
||||
} catch ( int sync4jerror ) {
|
||||
LOG.error( lastErrorMsg );
|
||||
} catch ( string *errmsg ) {
|
||||
|
@ -62,9 +67,11 @@ int main( int argc, char **argv )
|
|||
delete errmsg;
|
||||
} catch ( const char *errmsg ) {
|
||||
LOG.error( errmsg );
|
||||
} catch ( const string error ) {
|
||||
LOG.error( error.c_str() );
|
||||
} catch (...) {
|
||||
LOG.error( "unknown error" );
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue