2 * Copyright (C) 2005-2009 Patrick Ohly <patrick.ohly@gmx.de>
3 * Copyright (C) 2009 Intel Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) version 3.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #ifndef INCL_EVOLUTIONSYNCCLIENT
22 #define INCL_EVOLUTIONSYNCCLIENT
24 #include <syncevo/SmartPtr.h>
25 #include <syncevo/SyncConfig.h>
26 #include <syncevo/SyncML.h>
27 #include <syncevo/SynthesisEngine.h>
28 #include <syncevo/UserInterface.h>
35 #include <boost/smart_ptr.hpp>
36 #include <boost/signals2.hpp>
38 #include <syncevo/declarations.h>
44 class SyncSourceEvent;
47 * This is the main class inside SyncEvolution which
48 * looks at the configuration, activates all enabled
49 * sources and executes the synchronization.
51 * All interaction with the user (reporting progress, asking for
52 * passwords, ...) is done via virtual methods. The default
53 * implementation of those uses stdin/out.
56 class SyncContext : public SyncConfig {
58 * the string used to request a config,
59 * *not* the normalized config name itself;
60 * for that use SyncConfig::getConfigName()
62 const string m_server;
69 string m_localPeerContext; /**< context name (including @) if doing local sync */
70 string m_localClientRootPath;
72 bool m_serverAlerted; /**< sync was initiated by server (applies to client and server mode) */
74 std::string m_sessionID;
75 SharedBuffer m_initialMessage;
76 string m_initialMessageType;
77 string m_syncDeviceID;
79 FullProps m_configFilters;
81 boost::shared_ptr<TransportAgent> m_agent;
82 boost::shared_ptr<UserInterface> m_userInterface;
85 * a pointer to the active SourceList instance for this context if one exists;
86 * used for error handling in throwError() on the iPhone
88 SourceList *m_sourceListPtr;
91 * a pointer to the active SyncContext instance if one exists;
92 * set by sync() and/or SwapContext
94 static SyncContext *m_activeContext;
96 SyncContext *m_oldContext;
98 SwapContext(SyncContext *newContext) :
99 m_oldContext(SyncContext::m_activeContext) {
100 SyncContext::m_activeContext = newContext;
103 SyncContext::m_activeContext = m_oldContext;
108 * Connection to the Synthesis engine. Always valid in a
109 * constructed SyncContext. Use getEngine() to reference
112 SharedEngine m_engine;
115 * Synthesis session handle. Only valid while sync is running.
117 SharedSession m_session;
120 * installs session in SyncContext and removes it again
121 * when going out of scope
123 class SessionSentinel {
124 SyncContext &m_client;
126 SessionSentinel(SyncContext &client, SharedSession &session) :
128 m_client.m_session = session;
131 m_client.m_session.reset();
136 * The URL this SyncContext is actually using, since we may support multiple
137 * urls in the configuration.
139 string m_usedSyncURL;
141 /* True iff current sync session was triggered by us
142 * (such as in server alerted sync).
144 bool m_remoteInitiated;
147 * Common initialization code which needs to be done once
148 * at the start of main() in any application using SyncEvolution.
149 * For example, initializes (if applicable) glib and EDS.
151 * @param appname static string, must remain valid, defines name of executable (see g_set_prgname())
153 static void initMain(const char *appname);
156 * A signal invoked as part of initMain().
157 * Backends can connect to it to extend initMain().
159 typedef boost::signals2::signal<void (const char *appname)> InitMainSignal;
160 static InitMainSignal &GetInitMainSignal();
163 * A signal invoked each time a source has gone through a sync cycle.
165 typedef boost::signals2::signal<void (const std::string &name, const SyncSourceReport &source)> SourceSyncedSignal;
166 SourceSyncedSignal m_sourceSyncedSignal;
169 * true if binary was compiled as stable release
170 * (see gen-autotools.sh)
172 static bool isStableRelease();
175 * override stable release mode (for testing purposes)
177 static void setStableRelease(bool isStableRelease);
180 * SyncContext using a volatile config
186 * Constructor for syncing with a SyncML peer.
188 * @param peer identifies the client or server config to be used
189 * @param doLogging write additional log and datatbase files about the sync;
190 * true for regular syncs, false for debugging
192 SyncContext(const string &server,
193 bool doLogging = false);
196 * Constructor for client in a local sync.
198 * @param client identifies the client context to be used (@foobar)
199 * @param server identifies the server peer (foo@bar)
200 * @param rootPath use this directory as config directory for the
201 * peer-specific files (located inside peer directory
203 * @param agent transport agent, ready for communication with server
204 * @param doLogging write additional log and datatbase files about the sync
206 SyncContext(const string &client,
207 const string &server,
208 const string &rootPath,
209 const boost::shared_ptr<TransportAgent> &agent,
210 bool doLogging = false);
212 virtual ~SyncContext();
214 bool getQuiet() { return m_quiet; }
215 void setQuiet(bool quiet) { m_quiet = quiet; }
217 bool getDryRun() { return m_dryrun; }
218 void setDryRun(bool dryrun) { m_dryrun = dryrun; }
220 bool isLocalSync() const { return m_localSync; }
222 bool isServerAlerted() const { return m_serverAlerted; }
223 void setServerAlerted(bool serverAlerted) { m_serverAlerted = serverAlerted; }
225 boost::shared_ptr<UserInterface> getUserInterface() { return m_userInterface; }
226 void setUserInterface(const boost::shared_ptr<UserInterface> &userInterface) { m_userInterface = userInterface; }
228 /** use config UI owned by caller, without reference counting */
229 void setUserInterface(UserInterface *userInterface) { m_userInterface = boost::shared_ptr<UserInterface>(userInterface, NopDestructor()); }
232 * In contrast to getUserInterface(), this call here never returns NULL.
233 * If no UserInterface is currently set, then it returns
234 * a reference to a dummy instance which doesn't do anything.
236 UserInterface &getUserInterfaceNonNull();
239 * Running operations typically checks that a config really exists
240 * on disk. Setting false disables the check.
242 bool isConfigNeeded() const { return m_configNeeded; }
243 void setConfigNeeded(bool configNeeded) { m_configNeeded = configNeeded; }
246 * throws error if config is needed and not available
248 * @param operation a noun describing what is to be done next ("proceed with %s", operation)
250 void checkConfig(const std::string &operation) const;
253 * Sets configuration filters. Currently only used in local sync
254 * to configure the sync client.
256 void setConfigProps(const FullProps &props) { m_configFilters = props; }
257 const FullProps &getConfigProps() const { return m_configFilters; }
259 /** only for server: device ID of peer */
260 void setSyncDeviceID(const std::string &deviceID) { m_syncDeviceID = deviceID; }
261 std::string getSyncDeviceID() const { return m_syncDeviceID; }
264 * Use sendSAN as the first step is sync() if this is a server alerted sync.
265 * Prepare the san package and send the SAN request to the peer.
266 * Returns false if failed to get a valid client sync request
267 * otherwise put the client sync request into m_initialMessage which will
268 * be used to initalze the server via initServer(), then continue sync() to
269 * start the real sync serssion.
270 * @version indicates the SAN protocal version used (1.2 or 1.1/1.0)
272 bool sendSAN(uint16_t version);
275 * Initializes the session so that it runs as SyncML server once
276 * sync() is called. For this to work the first client message
277 * must be available already.
279 * @param sessionID session ID to be used by server
280 * @param data content of initial message sent by the client
281 * @param messageType content type set by the client
283 void initServer(const std::string &sessionID,
285 const std::string &messageType);
288 * Executes the sync, throws an exception in case of failure.
289 * Handles automatic backups and report generation.
291 * @retval complete sync report, skipped if NULL
292 * @return overall sync status, for individual sources see report
294 SyncMLStatus sync(SyncReport *report = NULL);
296 /** result of analyzeSyncMLMessage() */
297 struct SyncMLMessageInfo {
298 std::string m_deviceID;
300 /** a string representation of the whole structure for debugging */
301 std::string toString() { return std::string("deviceID ") + m_deviceID; }
305 * Instead or executing a sync, analyze the initial message
306 * without changing any local data. Returns once the LocURI =
307 * device ID of the client is known.
309 * @return device ID, empty if not in data
311 static SyncMLMessageInfo
312 analyzeSyncMLMessage(const char *data, size_t len,
313 const std::string &messageType);
316 * Convenience function, to be called inside a catch() block of
319 * Rethrows the exception to determine what it is, then logs it
320 * as an error and returns a suitable error code (usually a general
321 * STATUS_DATASTORE_FAILURE).
323 SyncMLStatus handleException();
326 * Determines the log directory of the previous sync (either in
327 * temp or logdir) and shows changes since then.
331 enum RestoreDatabase {
332 DATABASE_BEFORE_SYNC,
337 * Restore data of selected sources from before or after the given
338 * sync session, identified by absolute path to the log dir.
340 void restore(const string &dirname, RestoreDatabase database);
343 * fills vector with absolute path to information about previous
344 * sync sessions, oldest one first
346 void getSessions(vector<string> &dirs);
349 * fills report with information about previous session
350 * @return the peer name from the dir.
352 string readSessionInfo(const string &dir, SyncReport &report);
355 * fills report with information about local changes
357 * Only sync sources selected in the SyncContext
358 * constructor are checked. The local item changes will be set in
359 * the SyncReport's ITEM_LOCAL ITEM_ADDED/UPDATED/REMOVED.
361 * Some sync sources might not be able to report this
362 * information outside of a regular sync, in which case
363 * these fields are set to -1.
365 * Start and end times of the check are also reported.
367 void checkStatus(SyncReport &report);
370 * throws a StatusException with a local, fatal error with the given string
371 * or (on the iPhone, where exception handling is not
372 * supported by the toolchain) prints an error directly
375 * output format: <error>
377 * @param error a string describing the error
379 static void throwError(const string &error);
382 * throw an exception with a specific status code after an operation failed and
383 * remember that this instance has failed
385 * output format: <failure>
387 * @param status a more specific status code; other throwError() variants
388 * use STATUS_FATAL + sysync::LOCAL_STATUS_CODE, which is interpreted
389 * as a fatal local error
390 * @param action a string describing what was attempted *and* how it failed
392 static void throwError(SyncMLStatus status, const string &failure);
395 * throw an exception after an operation failed and
396 * remember that this instance has failed
398 * output format: <action>: <error string>
400 * @Param action a string describing the operation or object involved
401 * @param error the errno error code for the failure
403 static void throwError(const string &action, int error);
406 * An error handler which prints the error message and then
407 * stops the program. Never returns.
409 * The API was chosen so that it can be used as libebook/libecal
410 * "backend-dies" signal handler.
412 static void fatalError(void *object, const char *error);
415 * When using Evolution this function starts a background thread
416 * which drives the default event loop. Without that loop
417 * "backend-died" signals are not delivered. The problem with
418 * the thread is that it seems to interfere with gconf startup
419 * when added to the main() function of syncevolution. Therefore
420 * it is started by SyncSource::beginSync() (for unit
421 * testing of sync sources) and SyncContext::sync() (for
424 static void startLoopThread();
427 * Finds activated sync source by name. May return NULL
428 * if no such sync source was defined or is not currently
429 * instantiated. Pointer remains valid throughout the sync
430 * session. Called by Synthesis DB plugin to find active
433 * @param name can be both <SyncSource::getName()> as well as <prefix>_<SyncSource::getName()>
434 * (necessary when renaming sources in the Synthesis XML config)
436 * @TODO: roll SourceList into SyncContext and
437 * make this non-static
439 static SyncSource *findSource(const std::string &name);
440 static const char m_findSourceSeparator = '@';
443 * Find the active sync context for the given session.
445 * @param sessionName chosen by SyncEvolution and passed to
446 * Synthesis engine, which calls us back
447 * with it in SyncEvolution_Session_CreateContext()
448 * @return context or NULL if not found
450 static SyncContext *findContext(const char *sessionName);
452 SharedEngine getEngine() { return m_engine; }
453 const SharedEngine getEngine() const { return m_engine; }
455 bool getDoLogging() { return m_doLogging; }
458 * Returns the string used to select the peer config
459 * used by this instance.
461 * Note that this is not the same as a valid configuration
462 * name. For example "foo" might be matched against a
463 * "foo@bar" config by SyncConfig. Use SyncConfig::getConfigName()
464 * to get the underlying config.
466 std::string getPeer() { return m_server; }
469 * Handle for active session, may be NULL.
471 SharedSession getSession() { return m_session; }
473 bool getRemoteInitiated() {return m_remoteInitiated;}
474 void setRemoteInitiated(bool remote) {m_remoteInitiated = remote;}
477 * If called while a sync session runs,
478 * the engine will finish the session and then
479 * immediately try to run another one with
482 * Does nothing when called at the wrong time.
483 * There's no guarantee either that restarting is
486 static void requestAnotherSync();
489 * access to current set of sync sources, NULL if not instantiated yet
491 const std::vector<SyncSource *> *getSources() const;
494 /** exchange active Synthesis engine */
495 SharedEngine swapEngine(SharedEngine newengine) {
496 SharedEngine oldengine = m_engine;
497 m_engine = newengine;
501 /** sentinel class which creates, installs and removes a new
502 Synthesis engine for the duration of its own life time */
504 SyncContext &m_client;
505 SharedEngine m_oldengine;
508 SwapEngine(SyncContext &client) :
510 SharedEngine syncengine(m_client.createEngine());
511 m_oldengine = m_client.swapEngine(syncengine);
515 m_client.swapEngine(m_oldengine);
520 * Create a Synthesis engine for the currently active
521 * sources (might be empty!) and settings.
523 SharedEngine createEngine();
526 * Return skeleton Synthesis client XML configuration.
528 * The <scripting/>, <datatypes/>, <clientorserver/> elements (if
529 * present) are replaced by the caller with fragments found in the
530 * file system. When <datatypes> already has content, that content
531 * may contain <fieldlists/>, <profiles/>, <datatypedefs/>, which
532 * will be replaced by definitions gathered from backends.
534 * The default implementation of this function takes the configuration from
536 * - $(XDG_CONFIG_HOME)/syncevolution-xml
537 * - $(datadir)/syncevolution/xml
538 * Files with identical names are read from the first location where they
539 * are found. If $(SYNCEVOLUTION_XML_CONFIG_DIR) is set, then it overrides
540 * the previous two locations.
542 * The syncevolution.xml file is read from the first place where it is found.
543 * In addition, further .xml files in sub-directories are gathered and get
544 * inserted into the syncevolution.xml template.
546 * If none of these locations has XML configs, then builtin strings are
547 * used as fallback. This only works for mode == "client". Otherwise an
550 * @param mode "client" or "server"
551 * @retval xml is filled with Synthesis client config which may hav <datastore/>
552 * @retval rules remote rules which the caller needs for <clientorserver/>
553 * @retval configname a string describing where the config came from
555 virtual void getConfigTemplateXML(const string &mode,
562 * Return complete Synthesis XML configuration.
564 * Calls getConfigTemplateXML(), then fills in
565 * sync source XML fragments if necessary.
567 * @retval xml is filled with complete Synthesis client config
568 * @retval configname a string describing where the config came from
570 virtual void getConfigXML(string &xml, string &configname);
573 * Callback for derived classes: called after initializing the
574 * client, but before doing anything with its configuration.
575 * Can be used to override the client configuration.
577 virtual void prepare() {}
580 * instantiate transport agent
582 * Called by engine when it needs to exchange messages. The
583 * transport agent will be used throughout the sync session and
584 * unref'ed when no longer needed. At most one agent will be
585 * requested at a time. The transport agent is intentionally
586 * returned as a Boost shared pointer so that a pointer to a
587 * class with a different life cycle is possible, either by
588 * keeping a reference or by returning a shared_ptr where the
589 * destructor doesn't do anything.
591 * The agent must be ready for use:
592 * - HTTP specific settings must have been applied
593 * - the current SyncContext's timeout must have been
594 * installed via TransportAgent::setTimeout()
596 * The default implementation instantiates one of the builtin
597 * transport agents, depending on how it was compiled.
599 * @param gmainloop the GMainLoop to be used by transports, if not NULL;
600 * transports not supporting that should not be created;
601 * transports will increase the reference count for the loop
602 * @return transport agent
604 virtual boost::shared_ptr<TransportAgent> createTransportAgent(void *gmainloop);
605 virtual boost::shared_ptr<TransportAgent> createTransportAgent() { return createTransportAgent(NULL); }
608 * display a text message from the server
610 * Not really used by SyncML servers. Could be displayed in a
613 * @param message string with local encoding, possibly with line breaks
615 virtual void displayServerMessage(const string &message);
618 * display general sync session progress
620 * @param type PEV_*, see <synthesis/engine_defs.h>
621 * @param extra1 extra information depending on type
622 * @param extra2 extra information depending on type
623 * @param extra3 extra information depending on type
625 virtual void displaySyncProgress(sysync::TProgressEventEnum type,
626 int32_t extra1, int32_t extra2, int32_t extra3);
629 * An event plus its parameters, see Synthesis engine.
631 class SyncSourceEvent
634 sysync::TProgressEventEnum m_type;
635 int32_t m_extra1, m_extra2, m_extra3;
638 m_type(sysync::PEV_NOP)
641 SyncSourceEvent(sysync::TProgressEventEnum type,
654 * display sync source specific progress
656 * @param source source which is the target of the event
657 * @param event contains PEV_* and extra parameters, see <synthesis/engine_defs.h>
658 * @param flush if true, then bypass caching events and print directly
659 * @return true if the event was cached
661 virtual bool displaySourceProgress(SyncSource &source,
662 const SyncSourceEvent &event,
666 * report step command info
668 * Will be called after each step in step loop in SyncContext::doSync().
669 * This reports step command info.
670 * @param stepCmd step command enum value
672 virtual void reportStepCmd(sysync::uInt16 stepCmd) {}
675 /** initialize members as part of constructors */
679 * generate XML configuration and (re)initialize engine with it
681 void initEngine(bool logXML);
684 * the code common to init() and status():
685 * populate source list with active sources and open
687 void initSources(SourceList &sourceList);
690 * set m_localSync and m_localPeerContext
691 * @param config config name of peer
693 void initLocalSync(const string &config);
696 * called via pre-signal of m_startDataRead
698 void startSourceAccess(SyncSource *source);
701 * utility function for status() and getChanges():
702 * iterate over sources, check for changes and copy result
704 void checkSourceChanges(SourceList &sourceList, SyncReport &changes);
707 * A method to report sync is really successfully started.
708 * It happens at the same time SynthesDBPlugin starts to access source.
709 * For each sync, it is only called at most one time.
710 * The default action is nothing.
712 virtual void syncSuccessStart() { }
715 * sets up Synthesis session and executes it
717 SyncMLStatus doSync();
720 * directory for Synthesis client binfiles or
721 * Synthesis server textdb files, unique for each
724 string getSynthesisDatadir();
727 * return true if "delayedabort" session variable is true
729 bool checkForScriptAbort(SharedSession session);
731 // total retry duration
733 // message resend interval
735 // Current retry count
738 //a flag indicating whether it is the first time to start source access.
739 //It can be used to report infomation about a sync is successfully started.
740 bool m_firstSourceAccess;
742 // Cache for use in displaySourceProgress().
743 SyncSource *m_sourceProgress;
744 SyncSourceEvent m_sourceEvent;
748 * Returns the URL in the getSyncURL() list which is to be used
749 * for sync. The long term goal is to pick the first URL which
750 * uses a currently available transport; right now it simply picks
751 * the first supported one.
753 string getUsedSyncURL();
757 #endif // INCL_EVOLUTIONSYNCCLIENT