/* * Copyright (C) 2008 Patrick Ohly */ #ifndef INCL_SYNC_EVOLUTION_CONFIG # define INCL_SYNC_EVOLUTION_CONFIG #include "FilterConfigNode.h" #include "spds/AbstractSyncConfig.h" #include "spds/AbstractSyncSourceConfig.h" #include #include #include #include #include #include #include #include using namespace std; /** * @defgroup ConfigHandling Configuration Handling * @{ */ class EvolutionSyncSourceConfig; class PersistentEvolutionSyncSourceConfig; class ConfigTree; struct SyncSourceNodes; struct ConstSyncSourceNodes; /** * A property has a name and a comment. Derived classes might have * additional code to read and write the property from/to a * ConfigNode. They might also one or more of the properties * on the fly, therefore the virtual get methods which return a * string value and not just a reference. * * A default value is returned if the ConfigNode doesn't have * a value set (= empty string). Invalid values in the configuration * trigger an exception. Setting invalid values does not because * it is not known where the value comes from - the caller should * check it himself. */ class ConfigProperty { public: ConfigProperty(const string &name, const string &comment, const string &def = string("")) : m_obligatory(false), m_hidden(false), m_name(name), m_comment(boost::trim_right_copy(comment)), m_defValue(def) {} virtual ~ConfigProperty() {} virtual string getName() const { return m_name; } virtual string getComment() const { return m_comment; } virtual string getDefValue() const { return m_defValue; } /** * Check whether the given value is okay. * If not, then set an error string (one line, no punctuation). * * @return true if value is okay */ virtual bool checkValue(const string &value, string &error) const { return true; } /** split \n separated comment into lines without \n, appending them to commentLines */ static void splitComment(const string &comment, list &commentLines); bool isHidden() const { return m_hidden; } void setHidden(bool hidden) { m_hidden = hidden; } bool isObligatory() const { return m_obligatory; } void setObligatory(bool obligatory) { m_obligatory = obligatory; } /** set value unconditionally, even if it is not valid */ void setProperty(ConfigNode &node, const string &value) const { node.setProperty(getName(), value, getComment()); } void setProperty(FilterConfigNode &node, const string &value, bool temporarily = false) const { if (temporarily) { node.addFilter(m_name, value); } else { node.setProperty(m_name, value, getComment()); } } /** set default value of a property, marked as default unless forced setting */ void setDefaultProperty(ConfigNode &node, bool force) const { string defValue = getDefValue(); node.setProperty(m_name, defValue, getComment(), force ? NULL : &defValue); } /** * @retval isDefault return true if the node had no value set and * the default was returned instead */ virtual string getProperty(const ConfigNode &node, bool *isDefault = NULL) const { string name = getName(); string value = node.readProperty(name); if (value.size()) { string error; if (!checkValue(value, error)) { throwValueError(node, name, value, error); } if (isDefault) { *isDefault = false; } return value; } else { if (isDefault) { *isDefault = true; } return getDefValue(); } } protected: void throwValueError(const ConfigNode &node, const string &name, const string &value, const string &error) const; private: bool m_obligatory; bool m_hidden; const string m_name, m_comment, m_defValue; }; template class InitList : public list { public: InitList() {} InitList(const T &initialValue) { push_back(initialValue); } InitList &operator + (const T &rhs) { push_back(rhs); return *this; } InitList &operator += (const T &rhs) { push_back(rhs); return *this; } }; typedef InitList Aliases; typedef InitList Values; /** * A string property which maps multiple different possible value * strings to one generic value, ignoring the case. Values not listed * are passed through unchanged. The first value in the list of * aliases is the generic one. * * The addition operator is defined for the aliases so that they * can be constructed more easily. */ class StringConfigProperty : public ConfigProperty { public: StringConfigProperty(const string &name, const string &comment, const string &def = string(""), const Values &values = Values()) : ConfigProperty(name, comment, def), m_values(values) {} /** * @return false if aliases are defined and the string is not one of them */ bool normalizeValue(string &res) const { Values values = getValues(); BOOST_FOREACH(const Values::value_type &value, values) { BOOST_FOREACH(const string &alias, value) { if (boost::iequals(res, alias)) { res = *value.begin(); return true; } } } return values.empty(); } /** * This implementation accepts all values if no aliases * are given, otherwise the value must be part of the aliases. */ virtual bool checkValue(const string &propValue, string &error) const { Values values = getValues(); if (values.empty()) { return true; } ostringstream err; err << "not one of the valid values ("; bool firstval = true; BOOST_FOREACH(const Values::value_type &value, values) { if (!firstval) { err << ", "; } else { firstval = false; } bool firstalias = true; BOOST_FOREACH(const string &alias, value) { if (!firstalias) { err << " = "; } else { firstalias = false; } if (alias.empty()) { err << "\"\""; } else { err << alias; } if (boost::iequals(propValue, alias)) { return true; } } } err << ")"; error = err.str(); return false; } virtual string getProperty(const ConfigNode &node, bool *isDefault = NULL) const { string res = ConfigProperty::getProperty(node, isDefault); normalizeValue(res); return res; } protected: virtual Values getValues() const { return m_values; } private: const Values m_values; }; /** * Instead of reading and writing strings, this class interprets the content * as a specific type. */ template class TypedConfigProperty : public ConfigProperty { public: TypedConfigProperty(const string &name, const string &comment, const string &defValue = string("0")) : ConfigProperty(name, comment, defValue) {} /** * This implementation accepts all values that can be converted * to the required type. */ virtual bool checkValue(const string &value, string &error) const { istringstream in(value); T res; if (in >> res) { return true; } else { error = "cannot parse value"; return false; } } void setProperty(ConfigNode &node, const T &value) const { ostringstream out; out << value; node.setProperty(getName(), out.str(), getComment()); } void setProperty(FilterConfigNode &node, const T &value, bool temporarily = false) const { ostringstream out; out << value; if (temporarily) { node.addFilter(getName(), out.str()); } else { node.setProperty(getName(), out.str(), getComment()); } } T getProperty(ConfigNode &node, bool *isDefault = NULL) { string name = getName(); string value = node.readProperty(name); istringstream in(value); T res; if (value.empty()) { istringstream defStream(getDefValue()); defStream >> res; if (isDefault) { *isDefault = true; } return res; } else { if (!(in >> res)) { throwValueError(node, name, value, "cannot parse value"); } if (isDefault) { *isDefault = false; } return res; } } }; typedef TypedConfigProperty IntConfigProperty; typedef TypedConfigProperty UIntConfigProperty; typedef TypedConfigProperty LongConfigProperty; typedef TypedConfigProperty ULongConfigProperty; /** * This interface has to be provided by the user of the config * to let the config code interact with the user. */ class ConfigUserInterface { public: virtual ~ConfigUserInterface() {} /** * A helper function which interactively asks the user for * a certain password. May throw errors. * * @param descr A simple string explaining what the password is needed for, * e.g. "SyncML server". This string alone has to be enough * for the user to know what the password is for, i.e. the * string has to be unique. * @return entered password */ virtual string askPassword(const string &descr) = 0; }; class PasswordConfigProperty : public ConfigProperty { public: PasswordConfigProperty(const string &name, const string &comment, const string &def = string("")) : ConfigProperty(name, comment, def) {} /** * Check the password and cache the result. */ virtual void checkPassword(ConfigNode &node, ConfigUserInterface &ui, const string &descr, string &cachedPassword); /** * return the cached value if necessary and possible */ virtual string getCachedProperty(ConfigNode &node, const string &cachedPassword); }; /** * Instead of reading and writing strings, this class interprets the content * as boolean with T/F or 1/0 (default format). */ class BoolConfigProperty : public StringConfigProperty { public: BoolConfigProperty(const string &name, const string &comment, const string &defValue = string("F")) : StringConfigProperty(name, comment, defValue, Values() + (Aliases("1") + "T" + "TRUE") + (Aliases("0") + "F" + "FALSE")) {} void setProperty(ConfigNode &node, bool value) { StringConfigProperty::setProperty(node, value ? "1" : "0"); } void setProperty(FilterConfigNode &node, bool value, bool temporarily = false) { StringConfigProperty::setProperty(node, value ? "1" : "0", temporarily); } int getProperty(ConfigNode &node, bool *isDefault = NULL) { string res = ConfigProperty::getProperty(node, isDefault); return boost::iequals(res, "T") || boost::iequals(res, "TRUE") || atoi(res.c_str()) != 0; } }; /** * A registry for all properties which might be saved in the same ConfigNode. * Currently the same as a simple list. Someone else owns the instances. */ class ConfigPropertyRegistry : public list { public: /** case-insensitive search for property */ const ConfigProperty *find(const string &propName) const { BOOST_FOREACH(const ConfigProperty *prop, *this) { if (boost::iequals(prop->getName(), propName)) { return prop; } } return NULL; } }; /** * Store the current string value of a property in a cache * and return the "const char *" pointer that is expected by * the client library. */ class ConfigStringCache { public: const char *getProperty(const ConfigNode &node, const ConfigProperty &prop) { string value = prop.getProperty(node); return storeString(prop.getName(), value); } const char *storeString(const string &key, const string &value) { const string &entry = m_cache[key] = value; return entry.c_str(); } private: map m_cache; }; /** * This class implements the client library configuration interface * by mapping values to properties to entries in a ConfigTree. The * mapping is either the traditional one used by SyncEvolution <= 0.7 * and client library <= 6.5 or the new layout introduced with * SyncEvolution >= 0.8. If for a given server name the old config * exists, then it is used. Otherwise the new layout is used. * * This class can be instantiated on its own and then provides access * to properties actually stored in files. EvolutionSyncClient * inherits from this class so that a derived client has the chance to * override every single property (although it doesn't have to). * Likewise EvolutionSyncSource is derived from * EvolutionSyncSourceConfig. * * Properties can be set permanently (this changes the underlying * ConfigNode) and temporarily (this modifies the FilterConfigNode * which wraps the ConfigNode). * * The old layout is: * - $HOME/.sync4j/evolution//spds/syncml/config.txt * -- spds/sources//config.txt * --- changes_/config.txt * * The new layout is: * - ${XDG_CONFIG:-${HOME}/.config}/syncevolution/foo - base directory for server foo * -- config.ini - constant per-server settings * -- .internal.ini - read/write server properties - hidden from users because of the leading dot * -- sources/bar - base directory for source bar * --- config.ini - constant per-source settings * --- .internal.ini - read/write source properties * --- .changes_.ini - change tracking node (content under control of sync source) * * Because this class needs to handle different file layouts it always * uses a FileConfigTree instance. Other implementations would be * possible. */ class EvolutionSyncConfig : public AbstractSyncConfig { public: /** * Opens the configuration for a specific server, * searching for the config files in the usual * places. Will succeed even if config does not * yet exist: flushing such a config creates it. * * @param tree if non-NULL, then this is used * as configuration tree instead of * searching for it; always uses the * current layout in that tree */ EvolutionSyncConfig(const string &server, boost::shared_ptr tree = boost::shared_ptr()); /** * Creates a temporary configuration. * Can be copied around, but not flushed. */ EvolutionSyncConfig(); /** absolute directory name of the configuration root */ string getRootPath() const; typedef list< pair > ServerList; /** * returns list of servers in either the old (.sync4j) or * new config directory (.config), given as server name * and absolute root of config */ static ServerList getServers(); /** * returns list of available config templates, given as * server name and comment */ static ServerList getServerTemplates(); /** * Creates a new instance of a configuration template. * The result can be modified to set filters, but it * cannot be flushed. * * @return NULL if no such template */ static boost::shared_ptr createServerTemplate(const string &server); /** true if the main configuration file already exists */ bool exists() const; /** write changes */ void flush(); /** * A list of all properties. Can be extended by derived clients. */ static ConfigPropertyRegistry &getRegistry(); /** * Replaces the property filter of either the sync properties or * all sources. This can be used to e.g. temporarily override * the active sync mode. */ virtual void setConfigFilter(bool sync, const FilterConfigNode::ConfigFilter &filter) { if (sync) { m_configNode->setFilter(filter); } else { m_sourceFilter = filter; } } /** * Read-write access to all configurable properties of the server. * The visible properties are passed through the config filter, * which can be modified. */ virtual boost::shared_ptr getProperties(bool hidden = false) { if (hidden) { return boost::shared_ptr(new FilterConfigNode(m_hiddenNode)); } else { return m_configNode; } } virtual boost::shared_ptr getProperties(bool hidden = false) const { return const_cast(this)->getProperties(hidden); } /** * Returns a wrapper around all properties of the given source * which are saved in the config tree. Note that this is different * from the set of sync source configs used by the SyncManager: * the SyncManger uses the AbstractSyncSourceConfig. In * SyncEvolution those are implemented by the * EvolutionSyncSource's actually instantiated by * EvolutionSyncClient. Those are complete whereas * PersistentEvolutionSyncSourceConfig only provides access to a * subset of the properties. * * Can be called for sources which do not exist yet. */ virtual boost::shared_ptr getSyncSourceConfig(const string &name); virtual boost::shared_ptr getSyncSourceConfig(const string &name) const { return const_cast(this)->getSyncSourceConfig(name); } /** * Returns list of all configured (not active!) sync sources. */ virtual list getSyncSources() const; /** * Creates config nodes for a certain node. The nodes are not * yet created in the backend if they do not yet exist. * * @param name the name of the sync source * @param trackName additional part of the tracking node name (used for unit testing) */ SyncSourceNodes getSyncSourceNodes(const string &name, const string &trackName = ""); ConstSyncSourceNodes getSyncSourceNodes(const string &name, const string &trackName = "") const; /** * initialize all properties with their default value */ void setDefaults(); /** * create a new sync source configuration with default values */ void setSourceDefaults(const string &name); /** * Copy all registered properties (hidden and visible) and the * tracking node into the current config. This is done by reading * all properties from the source config, which implies the unset * properties will be set to their default values. The current * config is not cleared so additional, unregistered properties * (should they exist) will continue to exist unchanged. * * The current config still needs to be flushed to make the * changes permanent. * * @param sourceFilter if NULL, then copy all sources; if not NULL, * then only copy sources listed here */ void copy(const EvolutionSyncConfig &other, const set *sourceFilter); /** * @name Settings specific to SyncEvolution * * See the property definitions in SyncEvolutionConfig.cpp * for the user-visible explanations of * these settings. */ /**@{*/ virtual const char *getLogDir() const; virtual void setLogDir(const string &value, bool temporarily = false); virtual int getMaxLogDirs() const; virtual void setMaxLogDirs(int value, bool temporarily = false); virtual int getLogLevel() const; virtual void setLogLevel(int value, bool temporarily = false); /**@}*/ /** * @name Settings inherited from Funambol * * These settings are required by the Funambol C++ client library. * Some of them are hard-coded in this class. A derived class could * make them configurable again, should that be desired. */ /**@{*/ virtual AbstractSyncSourceConfig* getAbstractSyncSourceConfig(const char* name) const { return NULL; } virtual AbstractSyncSourceConfig* getAbstractSyncSourceConfig(unsigned int i) const { return NULL; } virtual unsigned int getAbstractSyncSourceConfigsCount() const { return 0; } virtual const char* getUsername() const; virtual void setUsername(const string &value, bool temporarily = false); virtual const char* getPassword() const; virtual void setPassword(const string &value, bool temporarily = false); /** * Look at the password setting and if it requires user interaction, * get it from the user. Then store it for later usage in getPassword(). * Without this call, getPassword() returns the original, unmodified * config string. */ virtual void checkPassword(ConfigUserInterface &ui); virtual bool getUseProxy() const; virtual void setUseProxy(bool value, bool temporarily = false); virtual const char* getProxyHost() const; virtual void setProxyHost(const string &value, bool temporarily = false); virtual int getProxyPort() const { return 0; } virtual const char* getProxyUsername() const; virtual void setProxyUsername(const string &value, bool temporarily = false); virtual const char* getProxyPassword() const; virtual void checkProxyPassword(ConfigUserInterface &ui); virtual void setProxyPassword(const string &value, bool temporarily = false); virtual const char* getSyncURL() const; virtual void setSyncURL(const string &value, bool temporarily = false); virtual const char* getClientAuthType() const; virtual void setClientAuthType(const string &value, bool temporarily = false); virtual bool getLoSupport() const; virtual void setLoSupport(bool value, bool temporarily = false); virtual unsigned long getMaxMsgSize() const; virtual void setMaxMsgSize(unsigned long value, bool temporarily = false); virtual unsigned int getMaxObjSize() const; virtual void setMaxObjSize(unsigned int value, bool temporarily = false); virtual unsigned long getReadBufferSize() const { return 0; } virtual const char* getSSLServerCertificates() const; virtual void setSSLServerCertificates(const string &value, bool temporarily = false); virtual bool getSSLVerifyServer() const; virtual void setSSLVerifyServer(bool value, bool temporarily = false); virtual bool getSSLVerifyHost() const; virtual void setSSLVerifyHost(bool value, bool temporarily = false); virtual bool getCompression() const; virtual void setCompression(bool value, bool temporarily = false); virtual unsigned int getResponseTimeout() const { return 0; } virtual const char* getDevID() const; virtual void setDevID(const string &value, bool temporarily = false); virtual bool getServerAuthRequired() const { return false; } virtual const char* getServerAuthType() const { return ""; } virtual const char* getServerPWD() const { return ""; } virtual const char* getServerID() const { return ""; } virtual const char* getUserAgent() const { return "SyncEvolution"; } virtual const char* getVerDTD() const { return "1.1"; } virtual const char* getMan() const { return "Patrick Ohly"; } virtual const char* getMod() const { return "SyncEvolution"; } virtual const char* getOem() const { return "Open Source"; } virtual const char* getFwv() const { return ""; } virtual const char* getHwv() const { return ""; } virtual const char* getDsV() const { return ""; } virtual const char* getSwv() const; virtual const char* getDevType() const; virtual bool getUtc() const { return true; } virtual bool getNocSupport() const { return false; } virtual const char* getServerNonce() const; virtual void setServerNonce(const char *value); virtual const char* getClientNonce() const; virtual void setClientNonce(const char *value); virtual const char* getDevInfHash() const; virtual void setDevInfHash(const char *value); /**@}*/ private: string m_server; bool m_oldLayout; string m_cachedPassword; string m_cachedProxyPassword; /** holds all config nodes relative to the root that we found */ boost::shared_ptr m_tree; /** access to global sync properties */ boost::shared_ptr m_configNode; boost::shared_ptr m_hiddenNode; /** temporary overrides for sync or sync source settings */ FilterConfigNode::ConfigFilter m_sourceFilter; mutable ConfigStringCache m_stringCache; static string getHome() { const char *homestr = getenv("HOME"); return homestr ? homestr : "."; } static string getOldRoot() { return getHome() + "/.sync4j/evolution"; } static string getNewRoot() { const char *xdg_root_str = getenv("XDG_CONFIG_HOME"); return xdg_root_str ? string(xdg_root_str) + "/syncevolution" : getHome() + "/.config/syncevolution"; } }; /** * This set of config nodes is to be used by EvolutionSyncSourceConfig * to accesss properties. */ struct SyncSourceNodes { /** * @param configNode node for user-visible properties * @param hiddenNode node for internal properties (may be the same as * configNode in old config layouts!) * @param trackingNode node for tracking changes (always different than the * other two nodes) */ SyncSourceNodes(const boost::shared_ptr &configNode, const boost::shared_ptr &hiddenNode, const boost::shared_ptr &trackingNode) : m_configNode(configNode), m_hiddenNode(hiddenNode), m_trackingNode(trackingNode) {} const boost::shared_ptr m_configNode; const boost::shared_ptr m_hiddenNode; const boost::shared_ptr m_trackingNode; }; struct ConstSyncSourceNodes { ConstSyncSourceNodes(const boost::shared_ptr &configNode, const boost::shared_ptr &hiddenNode, const boost::shared_ptr &trackingNode) : m_configNode(configNode), m_hiddenNode(hiddenNode), m_trackingNode(trackingNode) {} ConstSyncSourceNodes(const SyncSourceNodes &other) : m_configNode(other.m_configNode), m_hiddenNode(other.m_hiddenNode), m_trackingNode(other.m_trackingNode) {} const boost::shared_ptr m_configNode; const boost::shared_ptr m_hiddenNode; const boost::shared_ptr m_trackingNode; }; /** * This class maps per-source properties to ConfigNode properties. * Some properties are not configurable and have to be provided * by derived classes. */ class EvolutionSyncSourceConfig : public AbstractSyncSourceConfig { public: EvolutionSyncSourceConfig(const string &name, const SyncSourceNodes &nodes); static ConfigPropertyRegistry &getRegistry(); /** sync mode for sync sources */ static StringConfigProperty m_sourcePropSync; bool exists() const { return m_nodes.m_configNode->exists(); } /** * @name Settings specific to SyncEvolution SyncSources */ /**@{*/ virtual const char *getUser() const; virtual void setUser(const string &value, bool temporarily = false); const char *getPassword() const; virtual void setPassword(const string &value, bool temporarily = false); /** same as EvolutionSyncConfig::checkPassword() */ virtual void checkPassword(ConfigUserInterface &ui); virtual const char *getDatabaseID() const; virtual void setDatabaseID(const string &value, bool temporarily = false); /** * Returns the data source type configured as part of the given * configuration; different EvolutionSyncSources then check whether * they support that type. This call has to work before instantiating * a source and thus gets passed a node to read from. * * @return the pair of and the (possibly empty) * specified in the "type" property; see * sourcePropSourceType in SyncEvolutionConfig.cpp * for details */ static pair getSourceType(const SyncSourceNodes &nodes); static string getSourceTypeString(const SyncSourceNodes &nodes); virtual pair getSourceType() const; /** set the source type in [:format] style */ virtual void setSourceType(const string &value, bool temporarily = false); /**@}*/ /** * @name Calls which have to be implemented by each EvolutionSyncSource. */ /**@{*/ /** * 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; /** * A string representing the source types (with versions) supported by the SyncSource. * The string must be formatted as a sequence of "type:version" separated by commas ','. * For example: "text/x-vcard:2.1,text/vcard:3.0". * The version can be left empty, for example: "text/x-s4j-sifc:". * Supported types will be sent as part of the DevInf. */ virtual const char* getSupportedTypes() const = 0; /**@}*/ /** * @name Calls which usually do not have to be implemented by each EvolutionSyncSource. */ /**@{*/ /** * Returns the SyncSource URI: used in SyncML to address the data * on the server. * * Each URI has to be unique during a sync session, i.e. * two different sync sources cannot access the same data at * the same time. */ virtual const char* getURI() const; virtual void setURI(const string &value, bool temporarily = false); /** * Returns a comma separated list of the possible syncModes for the * SyncSource. Sync modes can be one of * - slow * - two-way * - one-way-from-server * - one-way-from-client * - refresh-from-server * - refresh-from-client * - one-way-from-server * - one-way-from-client * - addrchange (Funambol extension) * * This is hard-coded in SyncEvolution because changing it * wouldn't have any effect (IMHO). */ virtual const char* getSyncModes() const { return "slow,two-way,one-way-from-server,one-way-from-client,refresh-from-server,refresh-from-client"; } /** * Gets the default syncMode as one of the strings listed in setSyncModes. */ virtual const char* getSync() const; virtual void setSync(const string &value, bool temporarily = false); /** * Specifies how the content of an outgoing item should be * encoded by the client library if the sync source does not * set an encoding on the item that it created. Valid values * are listed in SyncItem::encodings. */ virtual const char* getEncoding() const; virtual void setEncoding(const string &value, bool temporarily = false); /** * Sets the last sync timestamp. Called by the sync engine at * the end of a sync. The client must save that modified * value; it is needed to decide during the next sync whether * an incremental sync is possible. * * SyncEvolution will reset this value when a SyncSource fails * and thus force a slow sync during the next sync. * * @param timestamp the last sync timestamp */ virtual unsigned long getLast() const; virtual void setLast(unsigned long timestamp); /** * "des" enables an encryption mode which only the Funambol server * understands. Not configurable in SyncEvolution unless a derived * SyncSource decides otherwise. */ virtual const char* getEncryption() const { return ""; } /** * Returns an array of CtCap with all the capabilities for this * source. The capabilities specify which parts of e.g. a vCard * the sync source supports. Not specifying this in detail by * returning an empty array implies that it supports all aspects. * This is the default implementation of this call. * * @return an ArrayList of CTCap */ virtual const ArrayList& getCtCaps() const { static const ArrayList dummy; return dummy; } /**@}*/ /** * @name Calls implemented by SyncEvolution. */ /**@{*/ virtual const char *getType() const { return getMimeType(); } virtual const char *getVersion() const { return getMimeVersion(); } virtual const char* getName() const { return m_name.c_str(); } /**@}*/ private: string m_name; SyncSourceNodes m_nodes; mutable ConfigStringCache m_stringCache; string m_cachedPassword; }; /** * Adds dummy implementations of the missing calls to * EvolutionSyncSourceConfig so that the other properties can be read. */ class PersistentEvolutionSyncSourceConfig : public EvolutionSyncSourceConfig { public: PersistentEvolutionSyncSourceConfig(const string &name, const SyncSourceNodes &nodes) : EvolutionSyncSourceConfig(name, nodes) {} virtual const char* getMimeType() const { return ""; } virtual const char* getMimeVersion() const { return ""; } virtual const char* getSupportedTypes() const { return ""; } }; /**@}*/ #endif