1198 lines
46 KiB
C++
1198 lines
46 KiB
C++
/*
|
|
* Copyright (C) 2005-2009 Patrick Ohly <patrick.ohly@gmx.de>
|
|
* Copyright (C) 2009 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) version 3.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#ifndef INCL_SYNCSOURCE
|
|
#define INCL_SYNCSOURCE
|
|
|
|
#include "config.h"
|
|
#include "SyncEvolutionConfig.h"
|
|
#include "Logging.h"
|
|
#include "SyncML.h"
|
|
using namespace SyncEvolution;
|
|
|
|
#include <synthesis/sync_declarations.h>
|
|
#include <synthesis/syerror.h>
|
|
|
|
#include <boost/function.hpp>
|
|
|
|
class SyncSource;
|
|
class SDKInterface;
|
|
|
|
/**
|
|
* This set of parameters always has to be passed when constructing
|
|
* SyncSource instances.
|
|
*/
|
|
struct SyncSourceParams {
|
|
/**
|
|
* @param name the name needed by SyncSource
|
|
* @param nodes a set of config nodes to be used by this source
|
|
* @param changeId a unique string constructed from an ID for SyncEvolution
|
|
* and the URL/database we synchronize against; can be
|
|
* used to do change tracking for that combination of
|
|
* peers
|
|
*/
|
|
SyncSourceParams(const string &name,
|
|
const SyncSourceNodes &nodes,
|
|
const string &changeId) :
|
|
m_name(name),
|
|
m_nodes(nodes),
|
|
m_changeId(stripChangeId(changeId))
|
|
{}
|
|
|
|
const string m_name;
|
|
const SyncSourceNodes m_nodes;
|
|
const string m_changeId;
|
|
|
|
/** remove special characters from change ID */
|
|
static string stripChangeId(const string changeId) {
|
|
string strippedChangeId = changeId;
|
|
size_t offset = 0;
|
|
while (offset < strippedChangeId.size()) {
|
|
switch (strippedChangeId[offset]) {
|
|
case ':':
|
|
case '/':
|
|
case '\\':
|
|
strippedChangeId.erase(offset, 1);
|
|
break;
|
|
default:
|
|
offset++;
|
|
}
|
|
}
|
|
return strippedChangeId;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The SyncEvolution core has no knowledge of existing SyncSource
|
|
* implementations. Implementations have to register themselves
|
|
* by instantiating this class exactly once with information
|
|
* about themselves.
|
|
*
|
|
* It is also possible to add configuration options. For that define a
|
|
* derived class. In its constructor use
|
|
* SyncSourceConfig::getRegistry() resp. SyncConfig::getRegistry() to
|
|
* define new configuration properties. The advantage of registering
|
|
* them is that the user interface will automatically handle them like
|
|
* the predefined ones. The namespace of these configuration options
|
|
* is shared by all sources and the core.
|
|
*
|
|
* For properties with arbitrary names use the
|
|
* SyncSourceNodes::m_trackingNode.
|
|
*/
|
|
class RegisterSyncSource
|
|
{
|
|
public:
|
|
/**
|
|
* Users select a SyncSource and its data format via the "type"
|
|
* config property. Backends have to add this kind of function to
|
|
* the SourceRegistry_t in order to be considered by the
|
|
* SyncSource creation mechanism.
|
|
*
|
|
* The function will be called to check whether the backend was
|
|
* meant by the user. It should return a new instance which will
|
|
* be freed by the caller or NULL if it does not support the
|
|
* selected type.
|
|
*
|
|
* Inactive sources should return the special InactiveSource
|
|
* pointer value if they recognize without a doubt that the user
|
|
* wanted to instantiate them: for example, an inactive
|
|
* EvolutionContactSource will return NULL for "addressbook" but
|
|
* InactiveSource for "evolution-contacts".
|
|
*/
|
|
typedef SyncSource *(*Create_t)(const SyncSourceParams ¶ms);
|
|
|
|
/** special return value of Create_t, not a real sync source! */
|
|
static SyncSource *const InactiveSource;
|
|
|
|
/**
|
|
* @param shortDescr a few words identifying the data to be synchronized,
|
|
* e.g. "Evolution Calendar"
|
|
* @param enabled true if the sync source can be instantiated,
|
|
* false if it was not enabled during compilation or is
|
|
* otherwise not functional
|
|
* @param create factory function for sync sources of this type
|
|
* @param typeDescr multiple lines separated by \n which get appended to
|
|
* the the description of the type property, e.g.
|
|
* "Evolution Memos = memo = evolution-memo\n"
|
|
* " plain text in UTF-8 (default) = text/plain\n"
|
|
* " iCalendar 2.0 = text/calendar\n"
|
|
* " The later format is not tested because none of the\n"
|
|
* " supported SyncML servers accepts it.\n"
|
|
* @param typeValues the config accepts multiple names for the same internal
|
|
* type string; this list here is added to that list of
|
|
* aliases. It should contain at least one unique string
|
|
* the can be used to pick this sync source among all
|
|
* SyncEvolution sync sources (testing, listing backends, ...).
|
|
* Example: Values() + (Aliases("Evolution Memos") + "evolution-memo")
|
|
*/
|
|
RegisterSyncSource(const string &shortDescr,
|
|
bool enabled,
|
|
Create_t create,
|
|
const string &typeDescr,
|
|
const Values &typeValues);
|
|
public:
|
|
const string m_shortDescr;
|
|
const bool m_enabled;
|
|
const Create_t m_create;
|
|
const string m_typeDescr;
|
|
const Values m_typeValues;
|
|
};
|
|
|
|
typedef list<const RegisterSyncSource *> SourceRegistry;
|
|
|
|
|
|
#ifdef ENABLE_INTEGRATION_TESTS
|
|
#include <ClientTest.h>
|
|
typedef ClientTest::Config ClientTestConfig;
|
|
#else
|
|
/**
|
|
* this class doesn't exist and cannot be referenced in code which is
|
|
* compiled without ENABLE_INTEGRATION_TEST, but we only need to
|
|
* declare a reference to it, so that's okay
|
|
*/
|
|
class ClientTestConfig;
|
|
class ClientTest;
|
|
#endif
|
|
|
|
/**
|
|
* In addition to registering the sync source itself by creating an
|
|
* instance of RegisterSyncSource, configurations for testing it can
|
|
* also be registered. A sync source which supports more than one data
|
|
* exchange format can register one configuration for each format, but
|
|
* not registering any configuration is also okay.
|
|
*
|
|
* This code depends on the C++ client library test framework and
|
|
* therefore CPPUnit. To avoid a hard dependency on that in the normal
|
|
* "syncevolution" binary, the actual usage of the test Config class
|
|
* is limited to the *Register.cpp files when compiling them for
|
|
* inclusion in the "client-test" binary, i.e., they are protected by
|
|
* #ifdef ENABLE_UNIT_TESTS.
|
|
*
|
|
* Sync sources have to work stand-alone without a full SyncClient
|
|
* configuration for all local tests. The minimal configuration prepared
|
|
* for the source includes:
|
|
* - a tracking node (as used f.i. by TrackingSyncSource) which
|
|
* points towards "~/.config/syncevolution/client-test-changes"
|
|
* - a unique change ID (as used f.i. by EvolutionContactSource)
|
|
* - a valid "evolutionsource" property in the config node, starting
|
|
* with the CLIENT_TEST_EVOLUTION_PREFIX env variable or (if that
|
|
* wasn't set) the "SyncEvolution_Test_" prefix
|
|
* - "evolutionuser/password" if CLIENT_TEST_EVOLUTION_USER/PASSWORD
|
|
* are set
|
|
*
|
|
* No other properties are set, which implies that currently sync sources
|
|
* which require further parameters cannot be tested.
|
|
*
|
|
* @warning There is a potential problem with the registration
|
|
* mechanism. Both the sync source tests as well as the CPPUnit tests
|
|
* derived from them are registrered when global class instances are
|
|
* initialized. If the RegisterTestEvolution instance in
|
|
* client-test-app.cpp is initialized *before* the sync source tests,
|
|
* then those won't show up in the test list. Currently the right
|
|
* order seems to be used, so everything works as expected.
|
|
*/
|
|
class RegisterSyncSourceTest
|
|
{
|
|
public:
|
|
/**
|
|
* This call is invoked after setting up the config with default
|
|
* values for the test cases selected via the constructor's
|
|
* testCaseName parameter (one of vcard21, vcard30, ical20, itodo20;
|
|
* see ClientTest in the Funambol client library for the current
|
|
* list).
|
|
*
|
|
* This call can then override any of the values or (if there
|
|
* are no predefined test cases) add them.
|
|
*
|
|
* The "type" property must select your sync source and the
|
|
* data format for the test.
|
|
*
|
|
* @retval config change any field whose default is not suitable
|
|
*/
|
|
virtual void updateConfig(ClientTestConfig &config) const = 0;
|
|
|
|
/**
|
|
* @param configName a unique string: the predefined names known by
|
|
* ClientTest::getTestData() are already used for the initial
|
|
* set of Evolution sync sources, for new sync sources
|
|
* build a string by combining them with the sync source name
|
|
* (e.g., "sqlite_vcard30")
|
|
* @param testCaseName a string recognized by ClientTest::getTestData() or an
|
|
* empty string if there are no predefined test cases
|
|
*/
|
|
RegisterSyncSourceTest(const string &configName,
|
|
const string &testCaseName);
|
|
virtual ~RegisterSyncSourceTest() {}
|
|
|
|
const string m_configName;
|
|
const string m_testCaseName;
|
|
};
|
|
|
|
class TestRegistry : public vector<const RegisterSyncSourceTest *>
|
|
{
|
|
public:
|
|
// TODO: using const RegisterSyncSourceTest * operator [] (int);
|
|
const RegisterSyncSourceTest * operator [] (const string &configName) const {
|
|
BOOST_FOREACH(const RegisterSyncSourceTest *test, *this) {
|
|
if (test->m_configName == configName) {
|
|
return test;
|
|
}
|
|
}
|
|
throw out_of_range(string("test config registry: ") + configName);
|
|
return NULL;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* a container for Synthesis XML config fragments
|
|
*
|
|
* Backends can define their own field lists, profiles, datatypes and
|
|
* remote rules. The name of each of these entities have to be unique:
|
|
* either prefix each name with the name of the backend or coordinate
|
|
* with other developers (e.g. regarding shared field lists).
|
|
*
|
|
* To add new items, add them to the respective hash in your backend's
|
|
* getDatastoreXML() or getSynthesisInfo() implementation. Both
|
|
* methods have default implementations: getSynthesisInfo() is called
|
|
* by the default getDatastoreXML() to provide some details and
|
|
* provides them based on the "type" configuration option.
|
|
*
|
|
* The default config XML contains several predefined items:
|
|
* - field lists: contacts, calendar, Note, bookmarks
|
|
* - profiles: vCard, vCalendar, Note, vBookmark
|
|
* - datatypes: vCard21, vCard30, vCalendar10, iCalendar20,
|
|
* note10/11 (no difference except the versioning!),
|
|
* vBookmark10
|
|
* - remote rule: EVOLUTION
|
|
*
|
|
* These items do not appear in the hashes, so avoid picking the same
|
|
* names. The entries of each hash has to be a well-formed XML
|
|
* element, their keys the name encoded in each XML element.
|
|
*/
|
|
struct XMLConfigFragments {
|
|
class mapping : public std::map<std::string, std::string> {
|
|
public:
|
|
string join() {
|
|
string res;
|
|
size_t len = 0;
|
|
BOOST_FOREACH(const value_type &entry, *this) {
|
|
len += entry.second.size() + 1;
|
|
}
|
|
res.reserve(len);
|
|
BOOST_FOREACH(const value_type &entry, *this) {
|
|
res += entry.second;
|
|
res += "\n";
|
|
}
|
|
return res;
|
|
}
|
|
} m_fieldlists,
|
|
m_profiles,
|
|
m_datatypes,
|
|
m_remoterules;
|
|
};
|
|
|
|
/**
|
|
* abstract base class for SyncSource with some common functionality
|
|
* and no data
|
|
*
|
|
* Used to implement and call that functionality in multiple derived
|
|
* classes, including situations where a derived class is derived from
|
|
* this base via different intermediate classes, therefore the
|
|
* need to keep it abstract.
|
|
*/
|
|
class SyncSourceBase : public Logger {
|
|
public:
|
|
virtual ~SyncSourceBase() {}
|
|
|
|
/** the unique name of the sync source (for example, "addressbook") */
|
|
virtual const char *getName() const { return "uninitialized SyncSourceBase"; }
|
|
|
|
/**
|
|
* Convenience function, to be called inside a catch() block of
|
|
* (or for) the sync source.
|
|
*
|
|
* Rethrows the exception to determine what it is, then logs it
|
|
* as an error and returns a suitable error code (usually a general
|
|
* STATUS_DATASTORE_FAILURE).
|
|
*/
|
|
SyncMLStatus handleException();
|
|
|
|
/**
|
|
* throw an exception after an operation failed
|
|
*
|
|
* output format: <source name>: <action>: <error string>
|
|
*
|
|
* @param action a string describing the operation or object involved
|
|
* @param error the errno error code for the failure
|
|
*/
|
|
void throwError(const string &action, int error);
|
|
|
|
/**
|
|
* throw an exception after an operation failed and
|
|
* remember that this instance has failed
|
|
*
|
|
* output format: <source name>: <failure>
|
|
*
|
|
* @param action a string describing what was attempted *and* how it failed
|
|
*/
|
|
void throwError(const string &failure);
|
|
|
|
/**
|
|
* The Synthesis engine only counts items which are deleted by the
|
|
* peer. Items deleted locally at the start of a
|
|
* refresh-from-server sync are not counted (and cannot be counted
|
|
* in all cases).
|
|
*
|
|
* Sync sources which want to have those items included in the
|
|
* sync statistics should count *all* deleted items using these
|
|
* methods. EvolutionSyncClient will use this number for
|
|
* refresh-from-server syncs.
|
|
*/
|
|
/**@{*/
|
|
virtual long getNumDeleted() const = 0;
|
|
virtual void setNumDeleted(long num) = 0;
|
|
virtual void incrementNumDeleted() = 0;
|
|
/**@}*/
|
|
|
|
/**
|
|
* Return Synthesis <datastore> XML fragment for this sync source.
|
|
* Must *not* include the <datastore> element; it is created by
|
|
* the caller.
|
|
*
|
|
* The default implementation returns a configuration for the
|
|
* SynthesisDBPlugin, which invokes SyncSource::Operations. Items
|
|
* are exchanged with the SyncsSource in the format defined by
|
|
* getSynthesisInfo(). The format used with the SyncML side is
|
|
* negotiated via the peer's capabilities, with the type defined
|
|
* in the configuration being the preferred one of the data store.
|
|
*
|
|
* See EvolutionSyncClient::getConfigXML() for details about
|
|
* predefined <datatype> entries that can be referenced here.
|
|
*
|
|
* @retval xml put content of <datastore>...</datastore> here
|
|
* @retval fragments the necessary definitions for the datastore have to be added here
|
|
*/
|
|
virtual void getDatastoreXML(string &xml, XMLConfigFragments &fragments);
|
|
|
|
/**
|
|
* Synthesis <datatype> name which matches the format used
|
|
* for importing and exporting items (exportData()).
|
|
* This is not necessarily the same format that is given
|
|
* to the Synthesis engine. If this internal format doesn't
|
|
* have a <datatype> in the engine, then an empty string is
|
|
* returned.
|
|
*/
|
|
virtual string getNativeDatatypeName();
|
|
|
|
/**
|
|
* Logging utility code.
|
|
*
|
|
* Every sync source adds "<name>" as prefix to its output.
|
|
* All calls are redirected into EvolutionSyncClient logger.
|
|
*/
|
|
virtual void messagev(Level level,
|
|
const char *prefix,
|
|
const char *file,
|
|
int line,
|
|
const char *function,
|
|
const char *format,
|
|
va_list args);
|
|
|
|
/**
|
|
* return Synthesis API pointer, if one currently is available
|
|
* (between SyncEvolution_Module_CreateContext() and
|
|
* SyncEvolution_Module_DeleteContext())
|
|
*/
|
|
virtual SDKInterface *getSynthesisAPI() const = 0;
|
|
|
|
|
|
protected:
|
|
struct SynthesisInfo {
|
|
/**
|
|
* name to use for MAKE/PARSETEXTWITHPROFILE,
|
|
* leave empty when acessing the field list directly
|
|
*/
|
|
std::string m_profile;
|
|
|
|
/** list of supported datatypes in "<use .../>" format */
|
|
std::string m_datatypes;
|
|
|
|
/** native datatype (see getNativeDatatypeName()) */
|
|
std::string m_native;
|
|
|
|
/** name of the field list used by the datatypes */
|
|
std::string m_fieldlist;
|
|
|
|
/**
|
|
* One or more Synthesis script statements, separated
|
|
* and terminated with a semicolon. Can be left empty.
|
|
*
|
|
* If not empty, then these statements are executed directly
|
|
* before converting the current item fields into
|
|
* a single string with MAKETEXTWITHPROFILE() in the sync source's
|
|
* <beforewritescript> (see SyncSourceBase::getDatastoreXML()).
|
|
*
|
|
* This value is currently only used by sync sources which
|
|
* set m_profile.
|
|
*/
|
|
std::string m_incomingScript;
|
|
|
|
/**
|
|
* Same as m_incomingScript, but used directly after
|
|
* converting a string into fields with PARSETEXTWITHPROFILE()
|
|
* in <afterreadscript>.
|
|
*/
|
|
std::string m_outgoingScript;
|
|
};
|
|
|
|
/**
|
|
* helper function for getDatastoreXML(): fill in information
|
|
* as necessary
|
|
*
|
|
* @retval fragments the necessary definitions for the other
|
|
* return values have to be added here
|
|
*/
|
|
virtual void getSynthesisInfo(SynthesisInfo &info,
|
|
XMLConfigFragments &fragments) = 0;
|
|
};
|
|
|
|
/**
|
|
* SyncEvolution accesses all sources through this interface.
|
|
*
|
|
* Certain functionality is optional or can be implemented in
|
|
* different ways. These methods are accessed through functors
|
|
* (function objects) which may be unset. The expected usage is that
|
|
* derived classes fill in the pieces that they provide by binding the
|
|
* functors to normal methods. For example, TrackingSyncSource
|
|
* provides a normal base class with pure virtual functions which have
|
|
* to be provided by users of that class.
|
|
*
|
|
* Error reporting is done via the Log class.
|
|
*/
|
|
class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, public SyncSourceReport
|
|
{
|
|
public:
|
|
SyncSource(const SyncSourceParams ¶ms) :
|
|
SyncSourceConfig(params.m_name, params.m_nodes),
|
|
m_numDeleted(0)
|
|
{
|
|
}
|
|
virtual ~SyncSource() {}
|
|
|
|
/**
|
|
* SyncSource implementations must register themselves here via
|
|
* RegisterSyncSource
|
|
*/
|
|
static SourceRegistry &getSourceRegistry();
|
|
|
|
/**
|
|
* SyncSource tests are registered here by the constructor of
|
|
* RegisterSyncSourceTest
|
|
*/
|
|
static TestRegistry &getTestRegistry();
|
|
|
|
struct Database {
|
|
Database(const string &name, const string &uri, bool isDefault = false) :
|
|
m_name( name ), m_uri( uri ), m_isDefault(isDefault) {}
|
|
string m_name;
|
|
string m_uri;
|
|
bool m_isDefault;
|
|
};
|
|
typedef vector<Database> Databases;
|
|
|
|
/**
|
|
* returns a list of all know data sources for the kind of items
|
|
* supported by this sync source
|
|
*/
|
|
virtual Databases getDatabases() = 0;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* The optional operations.
|
|
*
|
|
* All of them are guaranteed to happen between open() and
|
|
* close().
|
|
*
|
|
* They are all allowed to throw exceptions: the operations called
|
|
* by SyncEvolution then abort whatever SyncEvolution was doing
|
|
* and end in the normal exception handling. For the Synthesis
|
|
* operations, the bridge code in SynthesisDBPlugin code catches
|
|
* exceptions, logs them and translates them into Synthesis error
|
|
* codes, which are returned to the Synthesis engine.
|
|
*/
|
|
struct Operations {
|
|
/**
|
|
* Dump all data from source unmodified into the given directory.
|
|
* The ConfigNode can be used to store meta information needed for
|
|
* restoring that state. Both directory and node are empty.
|
|
* Information about the created backup is added to the
|
|
* report.
|
|
*
|
|
* Required for the backup/restore functionality in SyncEvolution,
|
|
* not for syncing itself.
|
|
*/
|
|
typedef void (BackupData_t)(const string &dirname, ConfigNode &node, BackupReport &report);
|
|
boost::function<BackupData_t> m_backupData;
|
|
|
|
/**
|
|
* Restore database from data stored in backupData().
|
|
*/
|
|
typedef void (RestoreData_t)(const string &dirname, const ConfigNode &node, bool dryrun, SyncSourceReport &report);
|
|
boost::function<RestoreData_t> m_restoreData;
|
|
|
|
/**
|
|
* initialize information about local changes and items
|
|
* as in beginSync() with all parameters set to true,
|
|
* but without changing the state of the underlying database
|
|
*
|
|
* This method will be called to check for local changes without
|
|
* actually running a sync, so there is no matching end call.
|
|
*
|
|
* There might be sources which don't support non-destructive
|
|
* change tracking (in other words, checking changes permanently
|
|
* modifies the state of the source and cannot be repeated).
|
|
* Such sources should leave the functor empty.
|
|
*/
|
|
typedef void (CheckStatus_t)(SyncSourceReport &local);
|
|
boost::function<CheckStatus_t> m_checkStatus;
|
|
|
|
/**
|
|
* Synthesis DB API callbacks. For documentation see the
|
|
* Synthesis API specification (PDF and/or sync_dbapi.h).
|
|
*
|
|
* Implementing this is necessary for SyncSources which want
|
|
* to be part of a sync session.
|
|
*/
|
|
/**@{*/
|
|
typedef sysync::TSyError (StartDataRead_t)(const char *lastToken, const char *resumeToken);
|
|
boost::function<StartDataRead_t> m_startDataRead;
|
|
|
|
typedef void (Callback_t)();
|
|
typedef boost::function<Callback_t> CallbackFunctor_t;
|
|
/** all of these functions will be called directly after
|
|
m_startDataRead() returned successfully */
|
|
std::list<CallbackFunctor_t> m_startSession;
|
|
|
|
typedef sysync::TSyError (EndDataRead_t)();
|
|
boost::function<EndDataRead_t> m_endDataRead;
|
|
|
|
typedef sysync::TSyError (StartDataWrite_t)();
|
|
boost::function<StartDataWrite_t> m_startDataWrite;
|
|
|
|
/** all of these functions will be called right
|
|
before m_endDataWrite() */
|
|
std::list<CallbackFunctor_t> m_endSession;
|
|
|
|
typedef sysync::TSyError (EndDataWrite_t)(bool success, char **newToken);
|
|
boost::function<EndDataWrite_t> m_endDataWrite;
|
|
|
|
/** the SynthesisDBPlugin is configured so that this operation
|
|
doesn't have to (and cannot) return the item data */
|
|
typedef sysync::TSyError (ReadNextItem_t)(sysync::ItemID aID,
|
|
sysync::sInt32 *aStatus, bool aFirst);
|
|
boost::function<ReadNextItem_t> m_readNextItem;
|
|
|
|
typedef sysync::TSyError (ReadItemAsKey_t)(sysync::cItemID aID, sysync::KeyH aItemKey);
|
|
boost::function<ReadItemAsKey_t> m_readItemAsKey;
|
|
|
|
typedef sysync::TSyError (InsertItemAsKey_t)(sysync::KeyH aItemKey, sysync::ItemID newID);
|
|
boost::function<InsertItemAsKey_t> m_insertItemAsKey;
|
|
|
|
typedef sysync::TSyError (UpdateItemAsKey_t)(sysync::KeyH aItemKey, sysync::cItemID aID, sysync::ItemID updID);
|
|
boost::function<UpdateItemAsKey_t> m_updateItemAsKey;
|
|
|
|
typedef sysync::TSyError (DeleteItem_t)(sysync::cItemID aID);
|
|
boost::function<DeleteItem_t> m_deleteItem;
|
|
/**@}*/
|
|
};
|
|
const Operations &getOperations() { return m_operations; }
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* return Synthesis API pointer, if one currently is available
|
|
* (between SyncEvolution_Module_CreateContext() and
|
|
* SyncEvolution_Module_DeleteContext())
|
|
*/
|
|
virtual SDKInterface *getSynthesisAPI() const;
|
|
|
|
/**
|
|
* change the Synthesis API that is used by the source
|
|
*/
|
|
void pushSynthesisAPI(sysync::SDK_InterfaceType *synthesisAPI);
|
|
|
|
/**
|
|
* remove latest Synthesis API and return to previous one (if any)
|
|
*/
|
|
void popSynthesisAPI();
|
|
|
|
/**
|
|
* factory function for a SyncSource that provides the
|
|
* source type specified in the params.m_nodes.m_configNode
|
|
*
|
|
* @param error throw a runtime error describing what the problem is if no matching source is found
|
|
* @return NULL if no source can handle the given type
|
|
*/
|
|
static SyncSource *createSource(const SyncSourceParams ¶ms,
|
|
bool error = true);
|
|
|
|
/**
|
|
* Factory function for a SyncSource with the given name
|
|
* and handling the kind of data specified by "type" (e.g.
|
|
* "Evolution Contacts:text/x-vcard").
|
|
*
|
|
* The source is instantiated with dummy configuration nodes under
|
|
* the pseudo server name "testing". This function is used for
|
|
* testing sync sources, not for real syncs. If the prefix is set,
|
|
* then <prefix>_<name>_1 is used as database, just as in the
|
|
* Client::Sync and Client::Source tests. Otherwise the default
|
|
* database is used.
|
|
*
|
|
* @param error throw a runtime error describing what the problem is if no matching source is found
|
|
* @return NULL if no source can handle the given type
|
|
*/
|
|
static SyncSource *createTestingSource(const string &name, const string &type, bool error,
|
|
const char *prefix = getenv("CLIENT_TEST_EVOLUTION_PREFIX"));
|
|
|
|
|
|
/* implementation of SyncSourceBase */
|
|
virtual const char * getName() const { return SyncSourceConfig::getName(); }
|
|
virtual long getNumDeleted() const { return m_numDeleted; }
|
|
virtual void setNumDeleted(long num) { m_numDeleted = num; }
|
|
virtual void incrementNumDeleted() { m_numDeleted++; }
|
|
|
|
protected:
|
|
Operations m_operations;
|
|
|
|
private:
|
|
/**
|
|
* Counter for items deleted in the source. Has to be incremented
|
|
* by RemoveAllItems() and DeleteItem(). This counter is used to
|
|
* update the Synthesis engine counter in those cases where the
|
|
* engine does not (refresh from server) or cannot
|
|
* (RemoveAllItems()) count the removals itself.
|
|
*/
|
|
long m_numDeleted;
|
|
|
|
/**
|
|
* Interface pointer for this sync source, allocated for us by the
|
|
* Synthesis engine and registered here by
|
|
* SyncEvolution_Module_CreateContext(). Only valid until
|
|
* SyncEvolution_Module_DeleteContext(), in other words, while
|
|
* the engine is running.
|
|
*/
|
|
std::vector<sysync::SDK_InterfaceType *> m_synthesisAPI;
|
|
};
|
|
|
|
/**
|
|
* Hooks up the Synthesis DB Interface start sync (BeginDataRead) and
|
|
* end sync (EndDataWrite) calls with virtual methods. Ensures that
|
|
* sleepSinceModification() is called.
|
|
*
|
|
* Inherit from this class in your sync source and call the init()
|
|
* method to use it.
|
|
*/
|
|
class SyncSourceSession : virtual public SyncSourceBase {
|
|
public:
|
|
/**
|
|
* called before Synthesis engine starts to ask for changes and item data
|
|
*
|
|
* See BeingDataRead for details.
|
|
*
|
|
* @param lastToken identifies the last completed sync
|
|
* @param resumeToken identifies a more recent sync which needs to be resumed;
|
|
* if not empty, then report changes made after that sync
|
|
* instead of the last completed sync
|
|
*/
|
|
virtual void beginSync(const std::string &lastToken, const std::string &resumeToken) = 0;
|
|
|
|
/**
|
|
* called after completing or suspending the current sync
|
|
*
|
|
* See EndDataWrite for details.
|
|
*
|
|
* @return a token identifying this sync session for a future beginSync()
|
|
*/
|
|
virtual std::string endSync(bool success) = 0;
|
|
|
|
/** set Synthesis DB Interface operations */
|
|
void init(SyncSource::Operations &ops);
|
|
private:
|
|
sysync::TSyError startDataRead(const char *lastToken, const char *resumeToken);
|
|
sysync::TSyError endDataWrite(bool success, char **newToken);
|
|
};
|
|
|
|
|
|
/**
|
|
* Implements the Synthesis DB Interface for reporting item changes
|
|
* (ReadNextItemAsKey) *without* actually delivering the item data.
|
|
*/
|
|
class SyncSourceChanges : virtual public SyncSourceBase {
|
|
public:
|
|
SyncSourceChanges();
|
|
|
|
enum State {
|
|
ANY,
|
|
NEW,
|
|
UPDATED,
|
|
DELETED,
|
|
MAX
|
|
};
|
|
|
|
/**
|
|
* Add the LUID of a NEW/UPDATED/DELETED item.
|
|
* If unspecified, the luid is added to the list of
|
|
* all items. This must be done *in addition* to adding
|
|
* the luid with a specific state.
|
|
*
|
|
* For example, the luid of an updated item should be added with
|
|
* addItem(luid [, ANY]) and again with addItem(luid, DELETED).
|
|
*
|
|
* The Synthesis engine does not need the list of deleted items
|
|
* and does not distinguish between added and updated items, so
|
|
* for syncing, adding DELETED items is optional and all items
|
|
* which are different from the last sync can be added as
|
|
* UPDATED. The client-test program expects that the information
|
|
* is provided precisely.
|
|
*
|
|
* @return true if the luid was already listed
|
|
*/
|
|
bool addItem(const string &luid, State state = ANY);
|
|
|
|
typedef std::set<std::string> Items_t;
|
|
const Items_t &getItems(State state) { return m_items[state]; }
|
|
const Items_t &getAllItems() const { return m_items[ANY]; }
|
|
const Items_t &getNewItems() const { return m_items[NEW]; }
|
|
const Items_t &getUpdatedItems() const { return m_items[UPDATED]; }
|
|
const Items_t &getDeletedItems() const { return m_items[DELETED]; }
|
|
|
|
/** set Synthesis DB Interface operations */
|
|
void init(SyncSource::Operations &ops);
|
|
|
|
private:
|
|
Items_t m_items[MAX];
|
|
bool m_first;
|
|
Items_t::const_iterator m_it;
|
|
|
|
sysync::TSyError iterate(sysync::ItemID aID,
|
|
sysync::sInt32 *aStatus,
|
|
bool aFirst);
|
|
};
|
|
|
|
/**
|
|
* Implements the Synthesis DB Interface for deleting an item
|
|
* (DeleteItem). Increments number of deleted items in
|
|
* SyncSourceBase.
|
|
*/
|
|
class SyncSourceDelete : virtual public SyncSourceBase {
|
|
public:
|
|
virtual void deleteItem(const string &luid) = 0;
|
|
|
|
/** set Synthesis DB Interface operations */
|
|
void init(SyncSource::Operations &ops);
|
|
|
|
private:
|
|
sysync::TSyError deleteItemSynthesis(sysync::cItemID aID);
|
|
};
|
|
|
|
/**
|
|
* an interface for reading and writing items in the internal
|
|
* format; see SyncSourceSerialize for an explanation
|
|
*/
|
|
class SyncSourceRaw : virtual public SyncSourceBase {
|
|
public:
|
|
class InsertItemResult {
|
|
public:
|
|
InsertItemResult() :
|
|
m_merged(false)
|
|
{}
|
|
|
|
/**
|
|
* @param luid the LUID after the operation; during an update the LUID must
|
|
* not be changed, so return the original one here
|
|
* @param revision the revision string after the operation; leave empty if not used
|
|
* @param merged set this to true if an existing item was updated instead of adding it
|
|
*/
|
|
InsertItemResult(const string &luid,
|
|
const string &revision,
|
|
bool merged) :
|
|
m_luid(luid),
|
|
m_revision(revision),
|
|
m_merged(merged)
|
|
{}
|
|
|
|
string m_luid;
|
|
string m_revision;
|
|
bool m_merged;
|
|
};
|
|
|
|
/** same as SyncSourceSerialize::insertItem(), but with internal format */
|
|
virtual InsertItemResult insertItemRaw(const std::string &luid, const std::string &item) = 0;
|
|
|
|
/** same as SyncSourceSerialize::readItem(), but with internal format */
|
|
virtual void readItemRaw(const std::string &luid, std::string &item) = 0;
|
|
};
|
|
|
|
/**
|
|
* Implements the Synthesis DB Interface for importing/exporting item
|
|
* data (ReadItemAsKey, InsertItemAsKey, UpdateItemAsKey) in such a
|
|
* way that the sync source only has to deal with a text
|
|
* representation of an item.
|
|
*
|
|
* There may be two such representations:
|
|
* - "engine format" is the one exchanged with the Synthesis engine
|
|
* - "internal or raw format" is a format that might better capture
|
|
* the internal representation and can be used for backup/restore
|
|
* and testing
|
|
*
|
|
* To give an example, the EvolutionMemoSource uses plain text as
|
|
* engine format and iCalendar 2.0 as raw format.
|
|
*
|
|
* The BackupData_t and RestoreData_t operations are implemented by
|
|
* this class using the internal format.
|
|
*
|
|
* The engine format must be something that the Synthesis engine can
|
|
* parse and generate, in other words, there must be a corresponding
|
|
* profile in the XML configuration. This class uses information
|
|
* provided by the sync source (mime type and version) and from the
|
|
* configuration (format selected by user) to generate the required
|
|
* XML configuration parts for common configurations (vCard,
|
|
* vCalendar, iCalendar, text). Special representations can be added
|
|
* to the global XML configuration by overriding default
|
|
* implementations provided in this class.
|
|
*
|
|
* InsertItemAsKey and UpdateItemAsKey are mapped to the same
|
|
* insertItem() call because in practice it can happen that a request
|
|
* to add an item must be turned into an update. For example, a
|
|
* meeting was imported both into the server and the client. A request
|
|
* to add the item again should be treated as an update, based on the
|
|
* unique iCalendar 2.0 LUID.
|
|
*/
|
|
class SyncSourceSerialize : virtual public SyncSourceBase, virtual public SyncSourceRaw {
|
|
public:
|
|
/**
|
|
* Returns the preferred mime type of the items handled by the sync source.
|
|
* Example: "text/x-vcard"
|
|
*/
|
|
virtual const char *getMimeType() const = 0;
|
|
|
|
/**
|
|
* Returns the version of the mime type used by client.
|
|
* Example: "2.1"
|
|
*/
|
|
virtual const char *getMimeVersion() const = 0;
|
|
|
|
/**
|
|
* returns the backend selection and configuration
|
|
*/
|
|
virtual SourceType getSourceType() const = 0;
|
|
|
|
/**
|
|
* Create or modify an item.
|
|
*
|
|
* The sync source should be flexible: if the LUID is non-empty, it
|
|
* shall modify the item referenced by the LUID. If the LUID is
|
|
* empty, the normal operation is to add it. But if the item
|
|
* already exists (e.g., a calendar event which was imported
|
|
* by the user manually), then the existing item should be
|
|
* updated also in the second case.
|
|
*
|
|
* Passing a LUID of an item which does not exist is an error.
|
|
* This error should be reported instead of covering it up by
|
|
* (re)creating the item.
|
|
*
|
|
* Errors are signaled by throwing an exception. Returning empty
|
|
* strings in the result is an error which triggers an "item could
|
|
* not be stored" error.
|
|
*
|
|
* @param luid identifies the item to be modified, empty for creating
|
|
* @param item contains the new content of the item, using the engine format
|
|
* @return the result of inserting the item
|
|
*/
|
|
virtual InsertItemResult insertItem(const std::string &luid, const std::string &item) = 0;
|
|
|
|
/**
|
|
* Return item data in engine format.
|
|
*
|
|
* @param luid identifies the item
|
|
* @retval item item data
|
|
*/
|
|
virtual void readItem(const std::string &luid, std::string &item) = 0;
|
|
|
|
/* implement SyncSourceRaw under the assumption that the internal and engine format are identical */
|
|
virtual InsertItemResult insertItemRaw(const std::string &luid, const std::string &item) { return insertItem(luid, item); }
|
|
virtual void readItemRaw(const std::string &luid, std::string &item) { return readItem(luid, item); }
|
|
|
|
/** set Synthesis DB Interface operations */
|
|
void init(SyncSource::Operations &ops);
|
|
|
|
protected:
|
|
/**
|
|
* used getMimeType(), getMimeVersion() and getSourceType()
|
|
* to provide the information necessary for automatic
|
|
* conversion to the sync source's internal item representation
|
|
*/
|
|
virtual void getSynthesisInfo(SynthesisInfo &info,
|
|
XMLConfigFragments &fragments);
|
|
private:
|
|
sysync::TSyError readItemAsKey(sysync::cItemID aID, sysync::KeyH aItemKey);
|
|
sysync::TSyError insertItemAsKey(sysync::KeyH aItemKey, sysync::cItemID aID, sysync::ItemID newID);
|
|
};
|
|
|
|
/**
|
|
* Implements change tracking based on a "revision" string, a string
|
|
* which is guaranteed to change automatically each time an item is
|
|
* modified. Backup/restore is optionally implemented by this class if
|
|
* pointers to SyncSourceRaw and SyncSourceDelete interfaces are
|
|
* passed to init(). For backup only the former is needed, for restore
|
|
* both.
|
|
*
|
|
* Potential implementations of the revision string are:
|
|
* - a modification time stamp
|
|
* - a hash value of a textual representation of the item
|
|
* (beware, such a hash might change as the textual representation
|
|
* changes even though the item is unchanged)
|
|
*
|
|
* Sync sources which want to use this functionality have to provide
|
|
* the following functionality by implementing the pure virtual
|
|
* functions below:
|
|
* - enumerate all existing items
|
|
* - provide LUID and the revision string
|
|
* The LUID must remain *constant* when making changes to an item,
|
|
* whereas the revision string must *change* each time the item is
|
|
* changed by anyone.
|
|
* Both can be arbitrary strings, but keeping them simple (printable
|
|
* ASCII, no white spaces, no equal sign) makes debugging simpler
|
|
* because they can be stored as they are as key/value pairs in the
|
|
* sync source's change tracking config node (the .other.ini files when
|
|
* using file-based configuration). More complex strings use escape
|
|
* sequences introduced with an exclamation mark for unsafe characters.
|
|
*
|
|
* Most of the functionality of this class must be activated
|
|
* explicitly as part of the life cycle of the sync source instance by
|
|
* calling detectChanges(), updateRevision() and deleteRevision().
|
|
*
|
|
* If the required interfaces are provided to init(), then backup/restore
|
|
* operations are set. init() also hooks into the session life cycle
|
|
* with an end callback that ensures that enough time passes at the end
|
|
* of the sync. This is important for sync sources which use time stamps
|
|
* as revision string. "enough time" is defined by a parameter to the
|
|
* init call.
|
|
*/
|
|
class SyncSourceRevisions : virtual public SyncSourceChanges, virtual public SyncSourceBase {
|
|
public:
|
|
typedef map<string, string> RevisionMap_t;
|
|
|
|
/**
|
|
* fills the complete mapping from UID to revision string of all
|
|
* currently existing items
|
|
*
|
|
* Usually both UID and revision string must be non-empty. The
|
|
* only exception is a refresh-from-client: in that case the
|
|
* revision string may be empty. The implementor of this call
|
|
* cannot know whether empty strings are allowed, therefore it
|
|
* should not throw errors when it cannot create a non-empty
|
|
* string. The caller of this method will detect situations where
|
|
* a non-empty string is necessary and none was provided.
|
|
*/
|
|
virtual void listAllItems(RevisionMap_t &revisions) = 0;
|
|
|
|
/**
|
|
* calculate changes, call when sync source is ready for
|
|
* listAllItems() and before changes are needed
|
|
*
|
|
* The trackingNode must be provided by the caller. It will
|
|
* be updated by each of the calls and must be stored by
|
|
* the caller.
|
|
*
|
|
* @param trackingNode a config node for exclusive use by this class
|
|
*/
|
|
void detectChanges(ConfigNode &trackingNode);
|
|
|
|
/**
|
|
* record that an item was added or updated
|
|
*
|
|
* @param old_luid empty for add, old LUID otherwise
|
|
* @param new_luid normally LUIDs must not change, but this call allows it
|
|
* @param revision revision string after change
|
|
*/
|
|
void updateRevision(ConfigNode &trackingNode,
|
|
const std::string &old_luid,
|
|
const std::string &new_luid,
|
|
const std::string &revision);
|
|
|
|
/**
|
|
* record that we deleted an item
|
|
*
|
|
* @param luid the obsolete LUID
|
|
*/
|
|
void deleteRevision(ConfigNode &trackingNode,
|
|
const std::string &luid);
|
|
|
|
/**
|
|
* set Synthesis DB Interface and backup/restore operations
|
|
* @param raw needed for backups; if NULL, no backups are made
|
|
* @param del needed for restores; if NULL, only backups are possible
|
|
* @param granularity time that has to pass between making a modification
|
|
* and checking for changes; this class ensures that
|
|
* at least this amount of time has passed before letting
|
|
* the session terminate. Delays in different source do
|
|
* not add up.
|
|
*/
|
|
void init(SyncSourceRaw *raw, SyncSourceDelete *del,
|
|
int granularity,
|
|
SyncSource::Operations &ops);
|
|
|
|
private:
|
|
SyncSourceRaw *m_raw;
|
|
SyncSourceDelete *m_del;
|
|
int m_revisionAccuracySeconds;
|
|
|
|
/**
|
|
* Dump all data from source unmodified into the given directory.
|
|
* The ConfigNode can be used to store meta information needed for
|
|
* restoring that state. Both directory and node are empty.
|
|
*/
|
|
void backupData(const string &dirname, ConfigNode &node, BackupReport &report);
|
|
|
|
/**
|
|
* Restore database from data stored in backupData(). Will be
|
|
* called inside open()/close() pair. beginSync() is *not* called.
|
|
*/
|
|
void restoreData(const string &dirname, const ConfigNode &node, bool dryrun, SyncSourceReport &report);
|
|
|
|
/**
|
|
* Increments the time stamp of the latest database modification,
|
|
* called automatically whenever revisions change.
|
|
*/
|
|
void databaseModified();
|
|
|
|
/** time stamp of latest database modification, for sleepSinceModification() */
|
|
time_t m_modTimeStamp;
|
|
void sleepSinceModification();
|
|
};
|
|
|
|
|
|
/**
|
|
* Common logging for sync sources.
|
|
*
|
|
* This class wraps the Synthesis DB functors that were set before
|
|
* calling its init() method. The wrappers then log a single line
|
|
* describing what is happening (adding/updating/removing)
|
|
* to which item (with a short item specific description extracted
|
|
* from the incoming item data or the backend).
|
|
*/
|
|
class SyncSourceLogging : public virtual SyncSourceBase
|
|
{
|
|
public:
|
|
/**
|
|
* wrap Synthesis DB Interface operations
|
|
*
|
|
* @param fields list of fields to read in getDescription()
|
|
* @param sep separator between non-empty fields
|
|
*/
|
|
void init(const std::list<std::string> &fields,
|
|
const std::string &sep,
|
|
SyncSource::Operations &ops);
|
|
|
|
/**
|
|
* Extract short description from Synthesis item data.
|
|
* The default implementation reads a list of fields
|
|
* as strings and concatenates the non-empty ones
|
|
* with a separator.
|
|
*
|
|
* @param aItemKey key for reading fields
|
|
* @return description, empty string will cause the ID of the item to be printed
|
|
*/
|
|
virtual std::string getDescription(sysync::KeyH aItemKey);
|
|
|
|
/**
|
|
* Extract short description from backend.
|
|
* Necessary for deleted items. The default implementation
|
|
* returns an empty string, so that implementing this
|
|
* is optional.
|
|
*
|
|
* @param luid LUID of the item to be deleted in the backend
|
|
* @return description, empty string will cause the ID of the item to be printed
|
|
*/
|
|
virtual std::string getDescription(const string &luid);
|
|
|
|
private:
|
|
std::list<std::string> m_fields;
|
|
std::string m_sep;
|
|
|
|
sysync::TSyError insertItemAsKey(sysync::KeyH aItemKey, sysync::ItemID newID,
|
|
const boost::function<SyncSource::Operations::InsertItemAsKey_t> &parent);
|
|
sysync::TSyError updateItemAsKey(sysync::KeyH aItemKey, sysync::cItemID aID, sysync::ItemID newID,
|
|
const boost::function<SyncSource::Operations::UpdateItemAsKey_t> &parent);
|
|
sysync::TSyError deleteItem(sysync::cItemID aID,
|
|
const boost::function<SyncSource::Operations::DeleteItem_t> &parent);
|
|
};
|
|
|
|
|
|
/**
|
|
* This is an interface definition that is expected by the client-test
|
|
* program. Part of the reason for this requirement is that the test
|
|
* program was originally written for the Funambol SyncSource API.
|
|
* The other reason is that the testing is based on importing/exporting
|
|
* items in the internal format of the sync source, which has to be
|
|
* text based or even MIMEDIR based (for tests involving synccompare).
|
|
*/
|
|
class TestingSyncSource : public SyncSource,
|
|
virtual public SyncSourceSession,
|
|
virtual public SyncSourceChanges,
|
|
virtual public SyncSourceDelete,
|
|
virtual public SyncSourceSerialize {
|
|
public:
|
|
TestingSyncSource(const SyncSourceParams ¶ms) :
|
|
SyncSource(params)
|
|
{
|
|
SyncSourceSession::init(m_operations);
|
|
SyncSourceChanges::init(m_operations);
|
|
SyncSourceDelete::init(m_operations);
|
|
SyncSourceSerialize::init(m_operations);
|
|
}
|
|
~TestingSyncSource() {}
|
|
|
|
virtual SourceType getSourceType() const { return SyncSourceConfig::getSourceType(); }
|
|
|
|
void removeAllItems() {
|
|
BOOST_FOREACH(const string &luid, getAllItems()) {
|
|
deleteItem(luid);
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif // INCL_SYNCSOURCE
|