2 * Copyright (C) 2009 Intel Corporation
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #include <syncevo/Logging.h>
26 #include <syncevo/LogStdout.h>
27 #include <syncevo/LogRedirect.h>
28 #include <syncevo/util.h>
29 #include <syncevo/SyncContext.h>
30 #include <syncevo/TransportAgent.h>
31 #include <syncevo/SoupTransportAgent.h>
32 #include <syncevo/SyncSource.h>
33 #include <syncevo/SyncML.h>
34 #include <syncevo/FileConfigNode.h>
35 #include <syncevo/Cmdline.h>
37 #include <synthesis/san.h>
53 #include <boost/shared_ptr.hpp>
54 #include <boost/weak_ptr.hpp>
55 #include <boost/noncopyable.hpp>
57 #include <glib-object.h>
58 #include <glib/gi18n.h>
59 #ifdef USE_GNOME_KEYRING
61 #include <gnome-keyring.h>
66 #include <libnotify/notify.h>
70 static DBusMessage *SyncEvoHandleException(DBusMessage *msg);
71 #define DBUS_CXX_EXCEPTION_HANDLER SyncEvoHandleException
72 #include "gdbus-cxx-bridge.h"
74 using namespace SyncEvo;
76 static GMainLoop *loop = NULL;
77 static bool shutdownRequested = false;
78 static LogRedirect *redirectPtr;
81 * Anything that can be owned by a client, like a connection
86 virtual ~Resource() {}
92 class DBusTransportAgent;
93 class DBusUserInterface;
96 class DBusSyncException : public DBusCXXException, public Exception
99 DBusSyncException(const std::string &file,
101 const std::string &what) : Exception(file, line, what)
104 * get exception name, used to convert to dbus error name
105 * subclasses should override it
107 virtual std::string getName() const { return "org.syncevolution.Exception"; }
109 virtual const char* getMessage() const { return Exception::what(); }
113 * exceptions classes deriving from DBusException
114 * org.syncevolution.NoSuchConfig
116 class NoSuchConfig: public DBusSyncException
119 NoSuchConfig(const std::string &file,
121 const std::string &error): DBusSyncException(file, line, error)
123 virtual std::string getName() const { return "org.syncevolution.NoSuchConfig";}
127 * org.syncevolution.NoSuchSource
129 class NoSuchSource : public DBusSyncException
132 NoSuchSource(const std::string &file,
134 const std::string &error): DBusSyncException(file, line, error)
136 virtual std::string getName() const { return "org.syncevolution.NoSuchSource";}
140 * org.syncevolution.InvalidCall
142 class InvalidCall : public DBusSyncException
145 InvalidCall(const std::string &file,
147 const std::string &error): DBusSyncException(file, line, error)
149 virtual std::string getName() const { return "org.syncevolution.InvalidCall";}
153 * org.syncevolution.SourceUnusable
154 * CheckSource will use this when the source cannot be used for whatever reason
156 class SourceUnusable : public DBusSyncException
159 SourceUnusable(const std::string &file,
161 const std::string &error): DBusSyncException(file, line, error)
163 virtual std::string getName() const { return "org.syncevolution.SourceUnusable";}
167 * implement syncevolution exception handler
168 * to cover its default implementation
170 static DBusMessage* SyncEvoHandleException(DBusMessage *msg)
172 /** give an opportunity to let syncevolution handle exception */
176 } catch (const dbus_error &ex) {
177 return b_dbus_create_error(msg, ex.dbusName().c_str(), "%s", ex.what());
178 } catch (const DBusCXXException &ex) {
179 return b_dbus_create_error(msg, ex.getName().c_str(), "%s", ex.getMessage());
180 } catch (const std::runtime_error &ex) {
181 return b_dbus_create_error(msg, "org.syncevolution.Exception", "%s", ex.what());
183 return b_dbus_create_error(msg, "org.syncevolution.Exception", "unknown");
188 * Utility class which makes it easier to work with g_timeout_add_seconds().
189 * Instantiate this class with a specific callback. Use boost::bind()
190 * to attach specific parameters to that callback. Then activate
191 * the timeout. Destructing this class will automatically remove
192 * the timeout and thus ensure that it doesn't trigger without
198 boost::function<bool ()> m_callback;
209 g_source_remove(m_tag);
214 * call the callback at regular intervals until it returns false
216 void activate(int seconds,
217 const boost::function<bool ()> &callback)
219 m_callback = callback;
220 m_tag = g_timeout_add_seconds(seconds, triggered, static_cast<gpointer>(this));
222 SE_THROW("g_timeout_add_seconds() failed");
227 * stop calling the callback, drop callback
232 g_source_remove(m_tag);
239 static gboolean triggered(gpointer data)
241 Timeout *me = static_cast<Timeout *>(data);
242 return me->m_callback();
247 * Implements the read-only methods in a Session and the Server.
248 * Only data is the server configuration name, everything else
249 * is created and destroyed inside the methods.
254 const std::string m_configName;
256 DBusServer &m_server;
258 ReadOperations(const std::string &config_name, DBusServer &server);
260 /** the double dictionary used to represent configurations */
261 typedef std::map< std::string, StringMap > Config_t;
263 /** the array of reports filled by getReports() */
264 typedef std::vector< StringMap > Reports_t;
266 /** the array of databases used by getDatabases() */
267 typedef SyncSource::Database SourceDatabase;
268 typedef SyncSource::Databases SourceDatabases_t;
270 /** implementation of D-Bus GetConfigs() */
271 void getConfigs(bool getTemplates, std::vector<std::string> &configNames);
273 /** implementation of D-Bus GetConfig() for m_configName as server configuration */
274 void getConfig(bool getTemplate,
277 /** implementation of D-Bus GetReports() for m_configName as server configuration */
278 void getReports(uint32_t start, uint32_t count,
281 /** Session.CheckSource() */
282 void checkSource(const string &sourceName);
284 /** Session.GetDatabases() */
285 void getDatabases(const string &sourceName, SourceDatabases_t &databases);
289 * This virtual function is used to let subclass set
290 * filters to config. Only used internally.
291 * Return true if filters exists and have been set.
292 * Otherwise, nothing is set to config
294 virtual bool setFilters(SyncConfig &config) { return false; }
297 * utility method which constructs a SyncConfig which references a local configuration (never a template)
299 * In general, the config must exist, except in two cases:
300 * - configName = @default (considered always available)
301 * - mustExist = false (used when reading a templates for a context which might not exist yet)
303 boost::shared_ptr<DBusUserInterface> getLocalConfig(const std::string &configName, bool mustExist = true);
307 * dbus_traits for SourceDatabase. Put it here for
308 * avoiding polluting gxx-dbus-bridge.h
310 template<> struct dbus_traits<ReadOperations::SourceDatabase> :
311 public dbus_struct_traits<ReadOperations::SourceDatabase,
312 dbus_member<ReadOperations::SourceDatabase, std::string, &ReadOperations::SourceDatabase::m_name,
313 dbus_member<ReadOperations::SourceDatabase, std::string, &ReadOperations::SourceDatabase::m_uri,
314 dbus_member_single<ReadOperations::SourceDatabase, bool, &ReadOperations::SourceDatabase::m_isDefault> > > >{};
317 * Automatic termination and track clients
318 * The dbus server will automatic terminate once it is idle in a given time.
319 * If any attached clients or connections, it never terminate.
320 * Once no actives, timer is started to detect the time of idle.
321 * Note that there will be less-than TERM_INTERVAL inaccuracy in seconds,
322 * that's because we do check every TERM_INTERVAL seconds.
331 * This callback is called as soon as we might have to terminate.
332 * If it finds that the server has been used in the meantime, it
333 * will simply set another timeout and check again later.
335 static gboolean checkCallback(gpointer data) {
336 AutoTerm *at = static_cast<AutoTerm*>(data);
338 // currently idle, but also long enough?
339 time_t now = time(NULL);
340 if (at->m_lastUsed + at->m_interval <= now) {
341 // yes, shut down event loop and daemon
342 shutdownRequested = true;
343 g_main_loop_quit(loop);
346 at->m_checkSource = g_timeout_add_seconds(at->m_lastUsed + at->m_interval - now,
351 // always remove the current timeout, its job is done
358 * If interval is less than 0, it means 'unlimited' and never terminate
360 AutoTerm(int interval) :
367 // increasing reference counts prevents shutdown forever
370 m_interval = interval;
378 g_source_remove(m_checkSource);
382 //increase the actives objects
383 void ref(int refs = 1) {
388 //decrease the actives objects
389 void unref(int refs = 1) {
398 * To be called each time the server interacts with a client,
399 * which includes adding or removing a client. If necessary,
400 * this installs a timeout to stop the daemon when it has been
406 // in use, don't need timeout
408 g_source_remove(m_checkSource);
412 // An already active timeout will trigger at the chosen time,
413 // then notice that the server has been used in the meantime and
414 // reset the timer. Therefore we don't have to remove it.
415 m_lastUsed = time(NULL);
416 if (!m_checkSource) {
417 m_checkSource = g_timeout_add_seconds(m_interval,
419 static_cast<gpointer>(this));
428 * Query bluetooth devices from org.bluez
429 * The basic workflow is:
430 * 1) get default adapter from bluez by calling 'DefaultAdapter' method of org.bluez.Manager
431 * 2) get all devices of the adapter by calling 'ListDevices' method of org.bluez.Adapter
432 * 3) iterate all devices and get properties for each one by calling 'GetProperties' method of org.bluez.Device.
433 * Then check its UUIDs whether it contains sync services and put it in the sync device list if it is
435 * To track changes of devices dynamically, here also listen signals from bluez:
436 * org.bluez.Manager - DefaultAdapterChanged: default adapter is changed and thus have to get its devices
437 * and update sync device list
438 * org.bluez.Adapter - DeviceCreated, DeviceRemoved: device is created or removed and device list is updated
439 * org.bluez.Device - PropertyChanged: property is changed and device information is changed and tracked
441 * This class is to manage querying bluetooth devices from org.bluez. Also
442 * it acts a proxy to org.bluez.Manager.
444 class BluezManager : public DBusRemoteObject {
446 BluezManager(DBusServer &server);
448 virtual const char *getDestination() const {return "org.bluez";}
449 virtual const char *getPath() const {return "/";}
450 virtual const char *getInterface() const {return "org.bluez.Manager";}
451 virtual DBusConnection *getConnection() const {return m_bluezConn.get();}
452 bool isDone() { return m_done; }
458 * This class acts a proxy to org.bluez.Adapter.
459 * Call methods of org.bluez.Adapter and listen signals from it
460 * to get devices list and track its changes
462 class BluezAdapter: public DBusRemoteObject
465 BluezAdapter (BluezManager &manager, const string &path);
467 virtual const char *getDestination() const {return "org.bluez";}
468 virtual const char *getPath() const {return m_path.c_str();}
469 virtual const char *getInterface() const {return "org.bluez.Adapter";}
470 virtual DBusConnection *getConnection() const {return m_manager.getConnection();}
471 void checkDone(bool forceDone = false)
473 if(forceDone || m_devReplies >= m_devNo) {
474 m_devReplies = m_devNo = 0;
475 m_manager.setDone(true);
477 m_manager.setDone(false);
481 std::vector<boost::shared_ptr<BluezDevice> >& getDevices() { return m_devices; }
484 /** callback of 'ListDevices' signal. Used to get all available devices of the adapter */
485 void listDevicesCb(const std::vector<DBusObject_t> &devices, const string &error);
487 /** callback of 'DeviceRemoved' signal. Used to track a device is removed */
488 void deviceRemoved(const DBusObject_t &object);
490 /** callback of 'DeviceCreated' signal. Used to track a new device is created */
491 void deviceCreated(const DBusObject_t &object);
493 BluezManager &m_manager;
494 /** the object path of adapter */
496 /** the number of device for the default adapter */
498 /** the number of devices having reply */
501 /** all available devices */
502 std::vector<boost::shared_ptr<BluezDevice> > m_devices;
504 /** represents 'DeviceRemoved' signal of org.bluez.Adapter*/
505 SignalWatch1<DBusObject_t> m_deviceRemoved;
506 /** represents 'DeviceAdded' signal of org.bluez.Adapter*/
507 SignalWatch1<DBusObject_t> m_deviceAdded;
509 friend class BluezDevice;
513 * This class acts a proxy to org.bluez.Device.
514 * Call methods of org.bluez.Device and listen signals from it
515 * to get properties of device and track its changes
517 class BluezDevice: public DBusRemoteObject
520 typedef map<string, boost::variant<vector<string>, string > > PropDict;
522 BluezDevice (BluezAdapter &adapter, const string &path);
524 virtual const char *getDestination() const {return "org.bluez";}
525 virtual const char *getPath() const {return m_path.c_str();}
526 virtual const char *getInterface() const {return "org.bluez.Device";}
527 virtual DBusConnection *getConnection() const {return m_adapter.m_manager.getConnection();}
528 string getMac() { return m_mac; }
531 * check whether the current device has sync service
532 * if yes, put it in the adapter's sync devices list
534 void checkSyncService(const std::vector<std::string> &uuids);
537 /** callback of 'GetProperties' method. The properties of the device is gotten */
538 void getPropertiesCb(const PropDict &props, const string &error);
540 /** callback of 'PropertyChanged' signal. Changed property is tracked */
541 void propertyChanged(const string &name, const boost::variant<vector<string>, string> &prop);
543 BluezAdapter &m_adapter;
544 /** the object path of the device */
546 /** name of the device */
548 /** mac address of the device */
550 /** whether the calling of 'GetProperties' is returned */
553 typedef SignalWatch2<string, boost::variant<vector<string>, string> > PropertySignal;
554 /** represents 'PropertyChanged' signal of org.bluez.Device */
555 PropertySignal m_propertyChanged;
557 friend class BluezAdapter;
561 * check whether the data is generated. If errors, force initilization done
563 void setDone(bool done) { m_done = done; }
565 /** callback of 'DefaultAdapter' method to get the default bluetooth adapter */
566 void defaultAdapterCb(const DBusObject_t &adapter, const string &error);
568 /** callback of 'DefaultAdapterChanged' signal to track changes of the default adapter */
569 void defaultAdapterChanged(const DBusObject_t &adapter);
571 DBusServer &m_server;
572 DBusConnectionPtr m_bluezConn;
573 boost::shared_ptr<BluezAdapter> m_adapter;
575 /** represents 'DefaultAdapterChanged' signal of org.bluez.Adapter*/
576 SignalWatch1<DBusObject_t> m_adapterChanged;
578 /** flag to indicate whether the calls are all returned */
583 * a listener to listen changes of session
584 * currently only used to track changes of running a sync in a session
586 class SessionListener
590 * method is called when a sync is successfully started.
591 * Here 'successfully started' means the synthesis engine starts
592 * to access the sources.
594 virtual void syncSuccessStart() {}
597 * method is called when a sync is done. Also
598 * sync status are passed.
600 virtual void syncDone(SyncMLStatus status) {}
602 virtual ~SessionListener() {}
606 * Manager to manage automatic sync.
607 * Once a configuration is enabled with automatic sync, possibly http or obex-bt or both, one or more
608 * tasks for different URLs are added in the task map, grouped by their intervals.
609 * A task have to be checked whether there is an existing same task in the working queue. Once actived,
610 * it is put in the working queue.
612 * At any time, there is at most one session for the first task. Once it is active by DBusServer,
613 * we prepare it and make it ready to run. After completion, a new session is created again for the
614 * next task. And so on.
616 * The DBusServer is in charge of dispatching requests from dbus clients and automatic sync tasks.
617 * See DBusServer::run().
619 * Here there are 3 scenarios which have been considered to do automatic sync right now:
620 * 1) For a config enables autosync, an interval has passed.
621 * 2) Once users log in or resume and an interval has passed. Not implemented yet.
622 * 3) Evolution data server notify any changes. Not implemented yet.
624 class AutoSyncManager : public SessionListener
626 DBusServer &m_server;
629 * A single task for automatic sync.
630 * Each task maintain one task for only one sync URL, which never combines
631 * more than one sync URLs. The difference from 'syncURL' property here is
632 * that different URLs may have different transports with different statuses.
633 * Another reason is that SyncContext only use the first URL if it has many sync
634 * URLs when running. So we split, schedule and process them one by one.
635 * Each task contains one peer name, peer duration and peer url.
636 * It is created in initialization and may be updated due to config change.
637 * It is scheduled by AutoSyncManager to be put in the working queue.
642 /** the peer name of a config */
644 /** the time that the peer must at least have been around (seconds) */
645 unsigned int m_delay;
646 /** the 'syncURL' used by synchronization. It always contains only one sync URL. */
649 AutoSyncTask(const string &peer, unsigned int delay, const string &url)
650 : m_peer(peer), m_delay(delay), m_url(url)
653 /** compare whether two tasks are the same. May refine it later with more information */
654 bool operator==(const AutoSyncTask &right) const
656 if(boost::iequals(m_peer, right.m_peer) &&
657 boost::iequals(m_url, right.m_url)) {
665 * AutoSyncTaskList is used to manage sync tasks which are grouped by the
666 * interval. Each list has one timeout gsource.
668 class AutoSyncTaskList : public list<AutoSyncTask>
670 AutoSyncManager &m_manager;
671 /** the interval used to create timeout source (seconds) */
672 unsigned int m_interval;
674 /** timeout gsource */
677 /** callback of timeout source */
678 static gboolean taskListTimeoutCb(gpointer data);
681 AutoSyncTaskList(AutoSyncManager &manager, unsigned int interval)
682 : m_manager(manager), m_interval(interval), m_source(0)
684 ~AutoSyncTaskList() {
686 g_source_remove(m_source);
690 /** create timeout source once all tasks are added */
691 void createTimeoutSource();
693 /** check task list and put task into working queue */
694 void scheduleTaskList();
699 * This class is to send notifications to notification server.
700 * Notifications are sent via 'send'. Once a new noficication is
701 * to be sent, the old notification will be closed and a new one
702 * is created for the new requirement.
710 /** callback of click button(actions) of notification */
711 static void notifyAction(NotifyNotification *notify, gchar *action, gpointer userData);
714 * send a notification in the notification server
715 * Action for 'view' may pop up a sync-ui, but needs some
716 * parameters. 'viewParams' is the params used by sync-ui.
718 void send(const char *summary, const char *body, const char *viewParams = NULL);
720 /** flag to indicate whether libnotify is initalized successfully */
723 /** flag to indicate whether libnotify accepts actions */
726 /** the current notification */
727 NotifyNotification *m_notification;
731 /** init a config and set up auto sync task for it */
732 void initConfig(const string &configName);
734 /** remove tasks from m_peerMap and m_workQueue created from the config */
735 void remove(const string &configName);
737 /** a map to contain all auto sync tasks. All initialized tasks are stored here.
738 * Tasks here are grouped by auto sync interval */
739 typedef std::map<unsigned int, boost::shared_ptr<AutoSyncTaskList> > PeerMap;
743 * a working queue that including tasks which are pending for doing sync.
744 * Tasks here are picked from m_peerMap and scheduled to do auto sync */
745 list<AutoSyncTask> m_workQueue;
748 * the current active task, which may own a session
750 boost::shared_ptr<AutoSyncTask> m_activeTask;
753 * the only session created for active task and is put in the session queue.
754 * at most one session at any time no matter how many tasks we actually have
756 boost::shared_ptr<Session> m_session;
758 /** the current sync of session is successfully started */
759 bool m_syncSuccessStart;
762 /** used to send notifications */
763 Notification m_notify;
767 * It reads all peers which are enabled to do auto sync and store them in
768 * the m_peerMap and then add timeout sources in the main loop to schedule
773 /** operations on tasks queue */
774 void clearAllTasks() { m_workQueue.clear(); }
776 /** check m_peerMap and put all tasks in it to working queue */
780 * add an auto sync task in the working queue
781 * Do check before adding a task in the working queue
782 * Return true if the task is added in the list.
784 bool addTask(const AutoSyncTask &syncTask);
786 /** find an auto sync task in the working queue or is running */
787 bool findTask(const AutoSyncTask &syncTask);
790 * check whether a task is suitable to put in the working queue
791 * Manager has the information needed to make the decision
793 bool taskLikelyToRun(const AutoSyncTask &syncTask);
796 AutoSyncManager(DBusServer &server)
797 : m_server(server), m_syncSuccessStart(false)
803 * prevent dbus server automatic termination when it has
804 * any auto sync task enabled in the configs.
805 * If returning true, prevent automatic termination.
807 bool preventTerm() { return !m_peerMap.empty(); }
810 * called when a config is changed. This causes re-loading the config
812 void update(const string &configName);
814 /* Is there any auto sync task in the queue? */
815 bool hasTask() { return !m_workQueue.empty(); }
818 * pick the front task from the working queue and create a session for it.
819 * The session won't be used to do sync until it is active so 'prepare' is
820 * for calling 'sync' to make the session ready to run
821 * If there has been a session for the front task, do nothing
825 /** check whether the active session is owned by Automatic Sync Manger */
826 bool hasActiveSession();
828 /** set config and run sync to make the session ready to run */
832 * Acts as a session listener to track sync statuses if the session is
833 * belonged to auto sync manager to do auto sync.
834 * Two methods to listen to session sync changes.
836 virtual void syncSuccessStart();
837 virtual void syncDone(SyncMLStatus status);
841 * A timer helper to check whether now is timeout according to
842 * user's setting. Timeout is calculated in milliseconds
845 timeval m_startTime; ///< start time
846 unsigned long m_timeoutMs; ///< timeout in milliseconds, set by user
849 * calculate duration between now and start time
850 * return value is in milliseconds
852 unsigned long duration(const timeval &minuend, const timeval &subtrahend)
854 unsigned long result = 0;
855 if(minuend.tv_sec > subtrahend.tv_sec ||
856 (minuend.tv_sec == subtrahend.tv_sec && minuend.tv_usec > subtrahend.tv_usec)) {
857 result = minuend.tv_sec - subtrahend.tv_sec;
859 result += (minuend.tv_usec - subtrahend.tv_usec) / 1000;
867 * @param timeoutMs timeout in milliseconds
869 Timer(unsigned long timeoutMs = 0) : m_timeoutMs(timeoutMs)
875 * reset the timer and mark start time as current time
877 void reset() { gettimeofday(&m_startTime, NULL); }
880 * check whether it is timeout
884 return timeout(m_timeoutMs);
888 * check whether the duration timer records is longer than the given duration
890 bool timeout(unsigned long timeoutMs)
893 gettimeofday(&now, NULL);
894 return duration(now, m_startTime) >= timeoutMs;
898 class PresenceStatus {
902 DBusServer &m_server;
904 /** two timers to record when the statuses of network and bt are changed */
909 /* The transport is not available (local problem) */
911 /* The peer is not contactable (remote problem) */
913 /* Not for sure whether the peer is presence but likely*/
919 typedef map<string, vector<pair <string, PeerStatus> > > StatusMap;
920 typedef pair<const string, vector<pair <string, PeerStatus> > > StatusPair;
921 typedef pair <string, PeerStatus> PeerStatusPair;
924 static std::string status2string (PeerStatus status) {
927 return "no transport";
930 return "not present";
936 return "invalid transport status";
938 // not reached, keep compiler happy
943 PresenceStatus (DBusServer &server)
944 :m_httpPresence (false), m_btPresence (false), m_initiated (false), m_server (server),
945 m_httpTimer(), m_btTimer()
957 /* Implement DBusServer::checkPresence*/
958 void checkPresence (const string &peer, string& status, std::vector<std::string> &transport);
960 void updateConfigPeers (const std::string &peer, const ReadOperations::Config_t &config);
962 void updatePresenceStatus (bool httpPresence, bool btPresence);
963 void updatePresenceStatus (bool newStatus, TransportType type);
965 bool getHttpPresence() { return m_httpPresence; }
966 bool getBtPresence() { return m_btPresence; }
967 Timer& getHttpTimer() { return m_httpTimer; }
968 Timer& getBtTimer() { return m_btTimer; }
972 * Implements org.moblin.connman.Manager
973 * GetProperty : getPropCb
974 * PropertyChanged: propertyChanged
976 class ConnmanClient : public DBusRemoteObject
979 ConnmanClient (DBusServer &server);
980 virtual const char *getDestination() const {return "org.moblin.connman";}
981 virtual const char *getPath() const {return "/";}
982 virtual const char *getInterface() const {return "org.moblin.connman.Manager";}
983 virtual DBusConnection *getConnection() const {return m_connmanConn.get();}
985 void propertyChanged(const string &name,
986 const boost::variant<vector<string>, string> &prop);
988 void getPropCb(const std::map <std::string, boost::variant <std::vector <std::string> > >& props, const string &error);
991 DBusServer &m_server;
992 DBusConnectionPtr m_connmanConn;
994 SignalWatch2 <string,boost::variant<vector<string>, string> > m_propertyChanged;
1000 * Implements the main org.syncevolution.Server interface.
1002 * All objects created by it get a reference to the creating
1003 * DBusServer instance so that they can call some of its
1004 * methods. Because that instance holds references to all
1005 * of these objects and deletes them before destructing itself,
1006 * that reference is guaranteed to remain valid.
1008 class DBusServer : public DBusObjectHelper,
1012 uint32_t m_lastSession;
1013 typedef std::list< std::pair< boost::shared_ptr<Watch>, boost::shared_ptr<Client> > > Clients_t;
1014 Clients_t m_clients;
1016 /* Event source that regurally pool network manager
1018 GLibEvent m_pollConnman;
1020 * The session which currently holds the main lock on the server.
1021 * To avoid issues with concurrent modification of data or configs,
1022 * only one session may make such modifications at a time. A
1023 * plain pointer which is reset by the session's deconstructor.
1025 * A weak pointer alone did not work because it does not provide access
1026 * to the underlying pointer after the last corresponding shared
1027 * pointer is gone (which triggers the deconstructing of the session).
1029 Session *m_activeSession;
1032 * The weak pointer that corresponds to m_activeSession.
1034 boost::weak_ptr<Session> m_activeSessionRef;
1037 * The running sync session. Having a separate reference to it
1038 * ensures that the object won't go away prematurely, even if all
1039 * clients disconnect.
1041 boost::shared_ptr<Session> m_syncSession;
1043 typedef std::list< boost::weak_ptr<Session> > WorkQueue_t;
1045 * A queue of pending, idle Sessions. Sorted by priority, most
1046 * important one first. Currently this is used to give client
1047 * requests a boost over remote connections and (in the future)
1050 * Active sessions are removed from this list and then continue
1051 * to exist as long as a client in m_clients references it or
1052 * it is the currently running sync session (m_syncSession).
1054 WorkQueue_t m_workQueue;
1057 * a hash of pending InfoRequest
1059 typedef std::map<string, boost::weak_ptr<InfoReq> > InfoReqMap;
1061 // hash map of pending info requests
1062 InfoReqMap m_infoReqMap;
1064 // the index of last info request
1065 uint32_t m_lastInfoReq;
1067 // a hash to represent matched templates for devices, the key is
1069 typedef std::map<string, boost::shared_ptr<SyncConfig::TemplateDescription> > MatchedTemplates;
1071 MatchedTemplates m_matchedTempls;
1073 BluezManager m_bluezManager;
1075 /** devices which have sync services */
1076 SyncConfig::DeviceList m_syncDevices;
1079 * Watch callback for a specific client or connection.
1081 void clientGone(Client *c);
1083 /** Server.GetCapabilities() */
1084 vector<string> getCapabilities();
1086 /** Server.GetVersions() */
1087 StringMap getVersions();
1089 /** Server.Attach() */
1090 void attachClient(const Caller_t &caller,
1091 const boost::shared_ptr<Watch> &watch);
1093 /** Server.Detach() */
1094 void detachClient(const Caller_t &caller);
1096 /** Server.DisableNotifications() */
1097 void disableNotifications(const Caller_t &caller,
1098 const string ¬ifications) {
1099 setNotifications(false, caller, notifications);
1102 /** Server.EnableNotifications() */
1103 void enableNotifications(const Caller_t &caller,
1104 const string ¬ifications) {
1105 setNotifications(true, caller, notifications);
1108 /** actual implementation of enable and disable */
1109 void setNotifications(bool enable,
1110 const Caller_t &caller,
1111 const string ¬ifications);
1113 /** Server.Connect() */
1114 void connect(const Caller_t &caller,
1115 const boost::shared_ptr<Watch> &watch,
1116 const StringMap &peer,
1117 bool must_authenticate,
1118 const std::string &session,
1119 DBusObject_t &object);
1121 /** Server.StartSession() */
1122 void startSession(const Caller_t &caller,
1123 const boost::shared_ptr<Watch> &watch,
1124 const std::string &server,
1125 DBusObject_t &object) {
1126 startSessionWithFlags(caller, watch, server, std::vector<std::string>(), object);
1129 /** Server.StartSessionWithFlags() */
1130 void startSessionWithFlags(const Caller_t &caller,
1131 const boost::shared_ptr<Watch> &watch,
1132 const std::string &server,
1133 const std::vector<std::string> &flags,
1134 DBusObject_t &object);
1136 /** Server.GetConfig() */
1137 void getConfig(const std::string &config_name,
1139 ReadOperations::Config_t &config)
1141 ReadOperations ops(config_name, *this);
1142 ops.getConfig(getTemplate , config);
1145 /** Server.GetReports() */
1146 void getReports(const std::string &config_name,
1147 uint32_t start, uint32_t count,
1148 ReadOperations::Reports_t &reports)
1150 ReadOperations ops(config_name, *this);
1151 ops.getReports(start, count, reports);
1154 /** Server.CheckSource() */
1155 void checkSource(const std::string &configName,
1156 const std::string &sourceName)
1158 ReadOperations ops(configName, *this);
1159 ops.checkSource(sourceName);
1162 /** Server.GetDatabases() */
1163 void getDatabases(const std::string &configName,
1164 const string &sourceName,
1165 ReadOperations::SourceDatabases_t &databases)
1167 ReadOperations ops(configName, *this);
1168 ops.getDatabases(sourceName, databases);
1171 void getConfigs(bool getTemplates,
1172 std::vector<std::string> &configNames)
1174 ReadOperations ops("", *this);
1175 ops.getConfigs(getTemplates, configNames);
1178 /** Server.CheckPresence() */
1179 void checkPresence(const std::string &server,
1180 std::string &status,
1181 std::vector<std::string> &transports);
1183 /** Server.GetSessions() */
1184 void getSessions(std::vector<DBusObject_t> &sessions);
1186 /** Server.InfoResponse() */
1187 void infoResponse(const Caller_t &caller,
1188 const std::string &id,
1189 const std::string &state,
1190 const std::map<string, string> &response);
1192 friend class InfoReq;
1194 /** emit InfoRequest */
1195 void emitInfoReq(const InfoReq &);
1197 /** get the next id of InfoRequest */
1198 std::string getNextInfoReq();
1200 /** remove InfoReq from hash map */
1201 void removeInfoReq(const InfoReq &req);
1203 /** Server.SessionChanged */
1204 EmitSignal2<const DBusObject_t &,
1205 bool> sessionChanged;
1207 /** Server.PresenceChanged */
1208 EmitSignal3<const std::string &,
1209 const std::string &,
1210 const std::string &> presence;
1213 * Server.TemplatesChanged, triggered each time m_syncDevices, the
1214 * input for the templates, is changed
1216 EmitSignal0 templatesChanged;
1219 * Server.ConfigChanged, triggered each time a session ends
1220 * which modified its configuration
1222 EmitSignal0 configChanged;
1224 /** Server.InfoRequest */
1225 EmitSignal6<const std::string &,
1226 const DBusObject_t &,
1227 const std::string &,
1228 const std::string &,
1229 const std::string &,
1230 const std::map<string, string> &> infoRequest;
1232 /** Server.LogOutput */
1233 EmitSignal3<const DBusObject_t &,
1235 const std::string &> logOutput;
1237 friend class Session;
1239 PresenceStatus m_presence;
1240 ConnmanClient m_connman;
1242 /** manager to automatic sync */
1243 AutoSyncManager m_autoSync;
1245 //automatic termination
1246 AutoTerm m_autoTerm;
1248 //records the parent logger, dbus server acts as logger to
1249 //send signals to clients and put logs in the parent logger.
1250 LoggerBase &m_parentLogger;
1253 * All active timeouts created by addTimeout().
1254 * Each timeout which requests to be not called
1255 * again will be removed from this list.
1257 list< boost::shared_ptr<Timeout> > m_timeouts;
1260 * called each time a timeout triggers,
1261 * removes those which are done
1263 bool callTimeout(const boost::shared_ptr<Timeout> &timeout, const boost::function<bool ()> &callback);
1266 DBusServer(GMainLoop *loop, const DBusConnectionPtr &conn, int duration);
1269 /** access to the GMainLoop reference used by this DBusServer instance */
1270 GMainLoop *getLoop() { return m_loop; }
1272 /** process D-Bus calls until the server is ready to quit */
1276 * look up client by its ID
1278 boost::shared_ptr<Client> findClient(const Caller_t &ID);
1281 * find client by its ID or create one anew
1283 boost::shared_ptr<Client> addClient(const DBusConnectionPtr &conn,
1285 const boost::shared_ptr<Watch> &watch);
1287 /** detach this resource from all clients which own it */
1288 void detach(Resource *resource);
1291 * Enqueue a session. Might also make it ready immediately,
1292 * if nothing else is first in the queue. To be called
1293 * by the creator of the session, *after* the session is
1296 void enqueue(const boost::shared_ptr<Session> &session);
1299 * Remove all sessions with this device ID from the
1300 * queue. If the active session also has this ID,
1301 * the session will be aborted and/or deactivated.
1303 int killSessions(const std::string &peerDeviceID);
1306 * Remove a session from the work queue. If it is running a sync,
1307 * it will keep running and nothing will change. Otherwise, if it
1308 * is "ready" (= holds a lock on its configuration), then release
1311 void dequeue(Session *session);
1314 * Checks whether the server is ready to run another session
1315 * and if so, activates the first one in the queue.
1320 * Invokes the given callback once in the given amount of seconds.
1321 * Keeps a copy of the callback. If the DBusServer is destructed
1322 * before that time, then the callback will be deleted without
1325 void addTimeout(const boost::function<bool ()> &callback,
1328 boost::shared_ptr<InfoReq> createInfoReq(const string &type,
1329 const std::map<string, string> ¶meters,
1330 const Session *session);
1331 void autoTermRef(int counts = 1) { m_autoTerm.ref(counts); }
1333 void autoTermUnref(int counts = 1) { m_autoTerm.unref(counts); }
1335 /** callback to reset for auto termination checking */
1336 void autoTermCallback() { m_autoTerm.reset(); }
1338 /** poll_nm callback for connman, used for presence detection*/
1339 void connmanCallback(const std::map <std::string, boost::variant <std::vector <std::string> > >& props, const string &error);
1341 PresenceStatus& getPresenceStatus() {return m_presence;}
1343 void clearPeerTempls() { m_matchedTempls.clear(); }
1344 void addPeerTempl(const string &templName, const boost::shared_ptr<SyncConfig::TemplateDescription> peerTempl);
1346 boost::shared_ptr<SyncConfig::TemplateDescription> getPeerTempl(const string &peer);
1349 * methods to operate device list. See DeviceList definition.
1350 * The device id here is the identifier of device, the same as definition in DeviceList.
1351 * In bluetooth devices, it refers to actually the mac address of the bluetooth.
1352 * The finger print and match mode is used to match templates.
1354 /** get sync devices */
1355 void getDeviceList(SyncConfig::DeviceList &devices);
1356 /** get a device according to device id. If not found, return false. */
1357 bool getDevice(const string &deviceId, SyncConfig::DeviceDescription &device);
1359 void addDevice(const SyncConfig::DeviceDescription &device);
1360 /** remove a device by device id. If not found, do nothing */
1361 void removeDevice(const string &deviceId);
1362 /** update a device with the given device information. If not found, do nothing */
1363 void updateDevice(const string &deviceId, const SyncConfig::DeviceDescription &device);
1365 /** emit a presence signal */
1366 void emitPresence(const string &server, const string &status, const string &transport)
1368 presence(server, status, transport);
1372 * Returns new unique session ID. Implemented with a running
1373 * counter. Checks for overflow, but not currently for active
1376 std::string getNextSession();
1378 AutoSyncManager &getAutoSyncManager() { return m_autoSync; }
1381 * false if any client requested suppression of notifications
1383 bool notificationsEnabled();
1386 * implement virtual method from LogStdout.
1387 * Not only print the message in the console
1388 * but also send them as signals to clients
1390 virtual void messagev(Level level,
1394 const char *function,
1400 * Tracks a single client and all sessions and connections that it is
1401 * connected to. Referencing them ensures that they stay around as
1406 DBusServer &m_server;
1408 typedef std::list< boost::shared_ptr<Resource> > Resources_t;
1409 Resources_t m_resources;
1411 /** counts how often a client has called Attach() without Detach() */
1414 /** current client setting for notifications (see HAS_NOTIFY) */
1415 bool m_notificationsEnabled;
1417 /** called 1 minute after last client detached from a session */
1418 static bool sessionExpired(const boost::shared_ptr<Session> &session);
1421 const Caller_t m_ID;
1423 Client(DBusServer &server,
1424 const Caller_t &ID) :
1427 m_notificationsEnabled(true),
1432 void increaseAttachCount() { ++m_attachCount; }
1433 void decreaseAttachCount() { --m_attachCount; }
1434 int getAttachCount() const { return m_attachCount; }
1436 void setNotificationsEnabled(bool enabled) { m_notificationsEnabled = enabled; }
1437 bool getNotificationsEnabled() const { return m_notificationsEnabled; }
1440 * Attach a specific resource to this client. As long as the
1441 * resource is attached, it cannot be freed. Can be called
1442 * multiple times, which means that detach() also has to be called
1443 * the same number of times to finally detach the resource.
1445 void attach(boost::shared_ptr<Resource> resource)
1447 m_resources.push_back(resource);
1451 * Detach once from the given resource. Has to be called as
1452 * often as attach() to really remove all references to the
1453 * session. It's an error to call detach() more often than
1456 void detach(Resource *resource);
1458 void detach(boost::shared_ptr<Resource> resource)
1460 detach(resource.get());
1464 * Remove all references to the given resource, regardless whether
1465 * it was referenced not at all or multiple times.
1467 void detachAll(Resource *resource) {
1468 Resources_t::iterator it = m_resources.begin();
1469 while (it != m_resources.end()) {
1470 if (it->get() == resource) {
1471 it = m_resources.erase(it);
1477 void detachAll(boost::shared_ptr<Resource> resource)
1479 detachAll(resource.get());
1483 * return corresponding smart pointer for a certain resource,
1484 * empty pointer if not found
1486 boost::shared_ptr<Resource> findResource(Resource *resource)
1488 for (Resources_t::iterator it = m_resources.begin();
1489 it != m_resources.end();
1491 if (it->get() == resource) {
1496 return boost::shared_ptr<Resource>();
1507 void set(const std::string &mode, const std::string &status, uint32_t error)
1515 std::string m_status;
1519 template<> struct dbus_traits<SourceStatus> :
1520 public dbus_struct_traits<SourceStatus,
1521 dbus_member<SourceStatus, std::string, &SourceStatus::m_mode,
1522 dbus_member<SourceStatus, std::string, &SourceStatus::m_status,
1523 dbus_member_single<SourceStatus, uint32_t, &SourceStatus::m_error> > > >
1526 struct SourceProgress
1530 m_prepareCount(-1), m_prepareTotal(-1),
1531 m_sendCount(-1), m_sendTotal(-1),
1532 m_receiveCount(-1), m_receiveTotal(-1)
1535 std::string m_phase;
1536 int32_t m_prepareCount, m_prepareTotal;
1537 int32_t m_sendCount, m_sendTotal;
1538 int32_t m_receiveCount, m_receiveTotal;
1541 template<> struct dbus_traits<SourceProgress> :
1542 public dbus_struct_traits<SourceProgress,
1543 dbus_member<SourceProgress, std::string, &SourceProgress::m_phase,
1544 dbus_member<SourceProgress, int32_t, &SourceProgress::m_prepareCount,
1545 dbus_member<SourceProgress, int32_t, &SourceProgress::m_prepareTotal,
1546 dbus_member<SourceProgress, int32_t, &SourceProgress::m_sendCount,
1547 dbus_member<SourceProgress, int32_t, &SourceProgress::m_sendTotal,
1548 dbus_member<SourceProgress, int32_t, &SourceProgress::m_receiveCount,
1549 dbus_member_single<SourceProgress, int32_t, &SourceProgress::m_receiveTotal> > > > > > > >
1553 * This class is mainly to implement two virtual functions 'askPassword'
1554 * and 'savePassword' of ConfigUserInterface. The main functionality is
1555 * to only get and save passwords in the gnome keyring.
1557 class DBusUserInterface : public SyncContext
1560 DBusUserInterface(const std::string &config);
1563 * Ask password from gnome keyring, if not found, empty string
1566 string askPassword(const string &passwordName,
1567 const string &descr,
1568 const ConfigPasswordKey &key);
1570 //save password to gnome keyring, if not successful, false is returned.
1571 bool savePassword(const string &passwordName,
1572 const string &password,
1573 const ConfigPasswordKey &key);
1576 * Read stdin via InfoRequest/Response.
1578 void readStdin(string &content);
1582 * A running sync engine which keeps answering on D-Bus whenever
1583 * possible and updates the Session while the sync runs.
1585 class DBusSync : public DBusUserInterface
1590 DBusSync(const std::string &config,
1595 virtual boost::shared_ptr<TransportAgent> createTransportAgent();
1596 virtual void displaySyncProgress(sysync::TProgressEventEnum type,
1597 int32_t extra1, int32_t extra2, int32_t extra3);
1598 virtual void displaySourceProgress(sysync::TProgressEventEnum type,
1600 int32_t extra1, int32_t extra2, int32_t extra3);
1602 virtual void reportStepCmd(sysync::uInt16 stepCmd);
1604 /** called when a sync is successfully started */
1605 virtual void syncSuccessStart();
1608 * Implement checkForSuspend and checkForAbort.
1609 * They will check whether dbus clients suspend
1610 * or abort the session in addition to checking
1611 * whether suspend/abort were requested via
1612 * signals, using SyncContext's signal handling.
1614 virtual bool checkForSuspend();
1615 virtual bool checkForAbort();
1616 virtual int sleep(int intervals);
1619 * Implement askPassword to retrieve password in gnome-keyring.
1620 * If not found, then ask it from dbus clients.
1622 string askPassword(const string &passwordName,
1623 const string &descr,
1624 const ConfigPasswordKey &key);
1628 * Hold progress info and try to estimate current progress
1630 class ProgressData {
1633 * big steps, each step contains many operations, such as
1634 * data prepare, message send/receive.
1635 * The partitions of these steps are based on profiling data
1636 * for many usage scenarios and different sync modes
1639 /** an invalid step */
1640 PRO_SYNC_INVALID = 0,
1642 * sync prepare step: do some preparations and checkings,
1643 * such as source preparation, engine preparation
1647 * session init step: transport connection set up,
1648 * start a session, authentication and dev info generation
1649 * normally it needs one time syncML messages send-receive.
1650 * Sometimes it may need messages send/receive many times to
1651 * handle authentication
1655 * prepare sync data and send data, also receive data from server.
1656 * Also may need send/receive messages more than one time if too
1658 * assume 5 items to be sent by default
1662 * item receive handling, send client's status to server and
1664 * assume 5 items to be received by default
1667 /** number of sync steps */
1671 * internal mode to represent whether it is possible that data is sent to
1672 * server or received from server. This could help remove some incorrect
1673 * hypothesis. For example, if only to client, then it is no data item
1674 * sending to server.
1678 INTERNAL_ONLY_TO_CLIENT = 1,
1679 INTERNAL_ONLY_TO_SERVER = 1 << 1,
1680 INTERNAL_TWO_WAY = 1 + (1 << 1)
1684 * treat a one-time send-receive without data items
1685 * as an internal standard unit.
1686 * below are ratios of other operations compared to one
1688 * These ratios might be dynamicall changed in the future.
1690 /** PRO_SYNC_PREPARE step ratio to standard unit */
1691 static const float PRO_SYNC_PREPARE_RATIO = 0.2;
1692 /** data prepare for data items to standard unit. All are combined by profiling data */
1693 static const float DATA_PREPARE_RATIO = 0.10;
1694 /** one data item send's ratio to standard unit */
1695 static const float ONEITEM_SEND_RATIO = 0.05;
1696 /** one data item receive&parse's ratio to standard unit */
1697 static const float ONEITEM_RECEIVE_RATIO = 0.05;
1698 /** connection setup to standard unit */
1699 static const float CONN_SETUP_RATIO = 0.5;
1700 /** assume the number of data items */
1701 static const int DEFAULT_ITEMS = 5;
1702 /** default times of message send/receive in each step */
1703 static const int MSG_SEND_RECEIVE_TIMES = 1;
1705 ProgressData(int32_t &progress);
1708 * change the big step
1710 void setStep(ProgressStep step);
1713 * calc progress when a message is sent
1718 * calc progress when a message is received from server
1723 * re-calc progress proportions according to syncmode hint
1724 * typically, if only refresh-from-client, then
1725 * client won't receive data items.
1727 void addSyncMode(SyncMode mode);
1730 * calc progress when data prepare for sending
1735 * calc progress when a data item is received
1737 void itemReceive(const string &source, int count, int total);
1741 /** update progress data */
1742 void updateProg(float ratio);
1744 /** dynamically adapt the proportion of each step by their current units */
1747 /** internally check sync mode */
1748 void checkInternalMode();
1750 /** get total units of current step and remaining steps */
1751 float getRemainTotalUnits();
1753 /** get default units of given step */
1754 static float getDefaultUnits(ProgressStep step);
1757 /** a reference of progress percentage */
1758 int32_t &m_progress;
1759 /** current big step */
1760 ProgressStep m_step;
1761 /** count of message send/receive in current step. Cleared in the start of a new step */
1763 /** internal sync mode combinations */
1765 /** proportions when each step is end */
1766 float m_syncProp[PRO_SYNC_TOTAL];
1767 /** remaining units of each step according to current step */
1768 float m_syncUnits[PRO_SYNC_TOTAL];
1769 /** proportion of a standard unit, may changes dynamically */
1771 /** current sync source */
1775 class CmdlineWrapper;
1778 * Represents and implements the Session interface. Use
1779 * boost::shared_ptr to track it and ensure that there are references
1780 * to it as long as the connection is needed.
1782 class Session : public DBusObjectHelper,
1784 private ReadOperations,
1785 private boost::noncopyable
1787 DBusServer &m_server;
1788 std::vector<std::string> m_flags;
1789 const std::string m_sessionID;
1790 std::string m_peerDeviceID;
1793 SharedBuffer m_initialMessage;
1794 string m_initialMessageType;
1796 boost::weak_ptr<Connection> m_connection;
1797 std::string m_connectionError;
1798 bool m_useConnection;
1800 /** temporary config changes */
1801 FilterConfigNode::ConfigFilter m_syncFilter;
1802 FilterConfigNode::ConfigFilter m_sourceFilter;
1803 typedef std::map<std::string, FilterConfigNode::ConfigFilter> SourceFilters_t;
1804 SourceFilters_t m_sourceFilters;
1806 /** whether dbus clients set temporary configs */
1810 * whether the dbus clients updated, removed or cleared configs,
1811 * ignoring temporary configuration changes
1816 * True while clients are allowed to make calls other than Detach(),
1817 * which is always allowed. Some calls are not allowed while this
1818 * session runs a sync, which is indicated by a non-NULL m_sync
1824 * True once done() was called.
1829 * Indicates whether this session was initiated by the peer or locally.
1831 bool m_remoteInitiated;
1834 * The SyncEvolution instance which currently prepares or runs a sync.
1836 boost::shared_ptr<DBusSync> m_sync;
1839 * the sync status for session
1842 SYNC_QUEUEING, ///< waiting to become ready for use
1843 SYNC_IDLE, ///< ready, session is initiated but sync not started
1844 SYNC_RUNNING, ///< sync is running
1845 SYNC_ABORT, ///< sync is aborting
1846 SYNC_SUSPEND, ///< sync is suspending
1847 SYNC_DONE, ///< sync is done
1851 /** current sync status */
1852 SyncStatus m_syncStatus;
1854 /** step info: whether engine is waiting for something */
1855 bool m_stepIsWaiting;
1858 * Priority which determines position in queue.
1859 * Lower is more important. PRI_DEFAULT is zero.
1865 /** progress data, holding progress calculation related info */
1866 ProgressData m_progData;
1868 typedef std::map<std::string, SourceStatus> SourceStatuses_t;
1869 SourceStatuses_t m_sourceStatus;
1872 typedef std::map<std::string, SourceProgress> SourceProgresses_t;
1873 SourceProgresses_t m_sourceProgress;
1875 /** timer for fire status/progress usages */
1876 Timer m_statusTimer;
1877 Timer m_progressTimer;
1880 string m_restoreDir;
1881 bool m_restoreBefore;
1882 /** the total number of sources to be restored */
1883 int m_restoreSrcTotal;
1884 /** the number of sources that have been restored */
1885 int m_restoreSrcEnd;
1894 static string runOpToString(RunOperation op);
1896 RunOperation m_runOperation;
1898 /** listener to listen to changes of sync */
1899 SessionListener *m_listener;
1901 /** Cmdline to execute command line args */
1902 boost::shared_ptr<CmdlineWrapper> m_cmdline;
1904 /** Session.Attach() */
1905 void attach(const Caller_t &caller);
1907 /** Session.Detach() */
1908 void detach(const Caller_t &caller);
1910 /** Session.GetStatus() */
1911 void getStatus(std::string &status,
1913 SourceStatuses_t &sources);
1914 /** Session.GetProgress() */
1915 void getProgress(int32_t &progress,
1916 SourceProgresses_t &sources);
1918 /** Session.Restore() */
1919 void restore(const string &dir, bool before,const std::vector<std::string> &sources);
1921 /** Session.checkPresence() */
1922 void checkPresence (string &status);
1924 /** Session.Execute() */
1925 void execute(const vector<string> &args, const map<string, string> &vars);
1928 * Must be called each time that properties changing the
1929 * overall status are changed. Ensures that the corresponding
1930 * D-Bus signal is sent.
1932 * Doesn't always send the signal immediately, because often it is
1933 * likely that more status changes will follow shortly. To ensure
1934 * that the "final" status is sent, call with flush=true.
1936 * @param flush force sending the current status
1938 void fireStatus(bool flush = false);
1939 /** like fireStatus() for progress information */
1940 void fireProgress(bool flush = false);
1942 /** Session.StatusChanged */
1943 EmitSignal3<const std::string &,
1945 const SourceStatuses_t &> emitStatus;
1946 /** Session.ProgressChanged */
1947 EmitSignal2<int32_t,
1948 const SourceProgresses_t &> emitProgress;
1950 static string syncStatusToString(SyncStatus state);
1954 * Sessions must always be held in a shared pointer
1955 * because some operations depend on that. This
1956 * constructor function here ensures that and
1957 * also adds a weak pointer to the instance itself,
1958 * so that it can create more shared pointers as
1961 static boost::shared_ptr<Session> createSession(DBusServer &server,
1962 const std::string &peerDeviceID,
1963 const std::string &config_name,
1964 const std::string &session,
1965 const std::vector<std::string> &flags = std::vector<std::string>());
1968 * automatically marks the session as completed before deleting it
1972 /** explicitly mark the session as completed, even if it doesn't get deleted yet */
1976 Session(DBusServer &server,
1977 const std::string &peerDeviceID,
1978 const std::string &config_name,
1979 const std::string &session,
1980 const std::vector<std::string> &flags = std::vector<std::string>());
1981 boost::weak_ptr<Session> m_me;
1987 PRI_CONNECTION = 10,
1992 * Default priority is 0. Higher means less important.
1994 void setPriority(int priority) { m_priority = priority; }
1995 int getPriority() const { return m_priority; }
1997 void initServer(SharedBuffer data, const std::string &messageType);
1998 void setConnection(const boost::shared_ptr<Connection> c) { m_connection = c; m_useConnection = c; }
1999 boost::weak_ptr<Connection> getConnection() { return m_connection; }
2000 bool useConnection() { return m_useConnection; }
2003 * After the connection closes, the Connection instance is
2004 * destructed immediately. This is necessary so that the
2005 * corresponding cleanup can remove all other classes
2006 * only referenced by the Connection.
2008 * This leads to the problem that an active sync cannot
2009 * query the final error code of the connection. This
2010 * is solved by setting a generic error code here when
2011 * the sync starts and overwriting it when the connection
2014 void setConnectionError(const std::string error) { m_connectionError = error; }
2015 std::string getConnectionError() { return m_connectionError; }
2018 DBusServer &getServer() { return m_server; }
2019 std::string getConfigName() { return m_configName; }
2020 std::string getSessionID() const { return m_sessionID; }
2021 std::string getPeerDeviceID() const { return m_peerDeviceID; }
2024 * TRUE if the session is ready to take over control
2026 bool readyToRun() { return (m_syncStatus != SYNC_DONE) && (m_runOperation != OP_NULL); }
2029 * transfer control to the session for the duration of the sync,
2030 * returns when the sync is done (successfully or unsuccessfully)
2035 * called when the session is ready to run (true) or
2036 * lost the right to make changes (false)
2038 void setActive(bool active);
2040 bool getActive() { return m_active; }
2042 void syncProgress(sysync::TProgressEventEnum type,
2043 int32_t extra1, int32_t extra2, int32_t extra3);
2044 void sourceProgress(sysync::TProgressEventEnum type,
2046 int32_t extra1, int32_t extra2, int32_t extra3);
2047 string askPassword(const string &passwordName,
2048 const string &descr,
2049 const ConfigPasswordKey &key);
2051 /** Session.GetFlags() */
2052 std::vector<std::string> getFlags() { return m_flags; }
2054 /** Session.GetConfigName() */
2055 std::string getNormalConfigName() { return SyncConfig::normalizeConfigString(m_configName); }
2057 /** Session.SetConfig() */
2058 void setConfig(bool update, bool temporary,
2059 const ReadOperations::Config_t &config);
2061 typedef StringMap SourceModes_t;
2062 /** Session.Sync() */
2063 void sync(const std::string &mode, const SourceModes_t &source_modes);
2064 /** Session.Abort() */
2066 /** Session.Suspend() */
2069 bool isSuspend() { return m_syncStatus == SYNC_SUSPEND; }
2070 bool isAbort() { return m_syncStatus == SYNC_ABORT; }
2073 * step info for engine: whether the engine is blocked by something
2074 * If yes, 'waiting' will be appended as specifiers in the status string.
2075 * see GetStatus documentation.
2077 void setStepInfo(bool isWaiting);
2079 /** sync is successfully started */
2080 void syncSuccessStart();
2083 * add a listener of the session. Old set listener is returned
2085 SessionListener* addListener(SessionListener *listener);
2087 void setRemoteInitiated (bool remote) { m_remoteInitiated = remote;}
2089 /** set m_syncFilter and m_sourceFilters to config */
2090 virtual bool setFilters(SyncConfig &config);
2094 * a wrapper to maintain the execution of command line
2095 * arguments from dbus clients. It is in charge of
2096 * redirecting output of cmd line to logging system.
2098 class CmdlineWrapper
2101 * inherit from stream buf to redirect the output.
2102 * Set a log until we gets a '\n' separator since we know
2103 * the command line message often ends with '\n'. The reason
2104 * is to avoid setting less characters in one log and thus
2105 * sending many signals to dbus clients.
2107 class CmdlineStreamBuf : public std::streambuf
2110 virtual ~CmdlineStreamBuf()
2112 //flush cached characters
2113 if(!m_str.empty()) {
2114 SE_LOG(LoggerBase::SHOW, NULL, NULL, "%s", m_str.c_str());
2119 * inherit from std::streambuf, all characters are cached in m_str
2120 * until a character '\n' is reached.
2122 virtual int_type overflow (int_type ch) {
2124 //don't append this character for logging system will append it
2125 SE_LOG(LoggerBase::SHOW, NULL, NULL, "%s", m_str.c_str());
2127 } else if (ch != EOF) {
2133 /** the cached output characters */
2137 /** streambuf used for m_cmdlineOutStream */
2138 CmdlineStreamBuf m_outStreamBuf;
2140 /** stream for command line out and err arguments */
2141 std::ostream m_cmdlineOutStream;
2144 * implement factory method to create DBusSync instances
2145 * This can check 'abort' and 'suspend' command from clients.
2147 class DBusCmdline : public Cmdline {
2150 DBusCmdline(Session &session,
2151 const vector<string> &args,
2154 :Cmdline(args, out, err), m_session(session)
2157 SyncContext* createSyncClient() {
2158 return new DBusSync(m_server, m_session);
2162 /** instance to run command line arguments */
2163 DBusCmdline m_cmdline;
2165 /** environment variables passed from client */
2166 map<string, string> m_envVars;
2170 * constructor to create cmdline instance.
2171 * Here just one stream is used and error message in
2172 * command line is output to this stream for it is
2173 * different from Logger::ERROR.
2175 CmdlineWrapper(Session &session,
2176 const vector<string> &args,
2177 const map<string, string> &vars)
2178 : m_cmdlineOutStream(&m_outStreamBuf),
2179 m_cmdline(session, args, m_cmdlineOutStream, m_cmdlineOutStream),
2183 bool parse() { return m_cmdline.parse(); }
2186 //temporarily set environment variables and restore them after running
2187 list<boost::shared_ptr<ScopedEnvChange> > changes;
2188 BOOST_FOREACH(const StringPair &var, m_envVars) {
2189 changes.push_back(boost::shared_ptr<ScopedEnvChange>(new ScopedEnvChange(var.first, var.second)));
2191 // exceptions must be handled (= printed) before returning,
2192 // so that our client gets the output
2194 if (!m_cmdline.run()) {
2195 SE_THROW_EXCEPTION(DBusSyncException, "command line execution failure");
2199 redirectPtr->flush();
2202 // always forward all currently pending redirected output
2203 // before closing the session
2204 redirectPtr->flush();
2207 bool configWasModified() { return m_cmdline.configWasModified(); }
2211 * Represents and implements the Connection interface.
2213 * The connection interacts with a Session by creating the Session and
2214 * exchanging data with it. For that, the connection registers itself
2215 * with the Session and unregisters again when it goes away.
2217 * In contrast to clients, the Session only keeps a weak_ptr, which
2218 * becomes invalid when the referenced object gets deleted. Typically
2219 * this means the Session has to abort, unless reconnecting is
2222 class Connection : public DBusObjectHelper, public Resource
2224 DBusServer &m_server;
2226 bool m_mustAuthenticate;
2228 SETUP, /**< ready for first message */
2229 PROCESSING, /**< received message, waiting for engine's reply */
2230 WAITING, /**< waiting for next follow-up message */
2231 FINAL, /**< engine has sent final reply, wait for ACK by peer */
2232 DONE, /**< peer has closed normally after the final reply */
2233 FAILED /**< in a failed state, no further operation possible */
2235 std::string m_failure;
2237 /** first parameter for Session::sync() */
2238 std::string m_syncMode;
2239 /** second parameter for Session::sync() */
2240 Session::SourceModes_t m_sourceModes;
2242 const std::string m_sessionID;
2243 boost::shared_ptr<Session> m_session;
2246 * main loop that our DBusTransportAgent is currently waiting in,
2247 * NULL if not waiting
2252 * get our peer session out of the DBusTransportAgent,
2253 * if it is currently waiting for us (indicated via m_loop)
2255 void wakeupSession();
2258 * buffer for received data, waiting here for engine to ask
2259 * for it via DBusTransportAgent::getReply().
2261 SharedBuffer m_incomingMsg;
2262 std::string m_incomingMsgType;
2265 std::vector <string> m_syncType;
2266 std::vector <uint32_t> m_contentType;
2267 std::vector <string> m_serverURI;
2271 * The content of a parsed SAN package to be processed via
2274 boost::shared_ptr <SANContent> m_SANContent;
2275 std::string m_peerBtAddr;
2278 * records the reason for the failure, sends Abort signal and puts
2279 * the connection into the FAILED state.
2281 void failed(const std::string &reason);
2284 * returns "<description> (<ID> via <transport> <transport_description>)"
2286 static std::string buildDescription(const StringMap &peer);
2288 /** Connection.Process() */
2289 void process(const Caller_t &caller,
2290 const std::pair<size_t, const uint8_t *> &message,
2291 const std::string &message_type);
2292 /** Connection.Close() */
2293 void close(const Caller_t &caller,
2295 const std::string &error);
2296 /** wrapper around sendAbort */
2298 /** Connection.Abort */
2299 EmitSignal0 sendAbort;
2301 /** Connection.Reply */
2302 EmitSignal5<const std::pair<size_t, const uint8_t *> &,
2303 const std::string &,
2306 const std::string &> reply;
2308 friend class DBusTransportAgent;
2311 const std::string m_description;
2313 Connection(DBusServer &server,
2314 const DBusConnectionPtr &conn,
2315 const std::string &session_num,
2316 const StringMap &peer,
2317 bool must_authenticate);
2321 /** session requested by us is ready to run a sync */
2324 /** connection is no longer needed, ensure that it gets deleted */
2327 /** peer is not trusted, must authenticate as part of SyncML */
2328 bool mustAuthenticate() const { return m_mustAuthenticate; }
2332 * A proxy for a Connection instance. The Connection instance can go
2333 * away (weak pointer, must be locked and and checked each time it is
2334 * needed). The agent must remain available as long as the engine
2335 * needs and basically becomes unusuable once the connection dies.
2337 * Reconnecting is not currently supported.
2339 class DBusTransportAgent : public TransportAgent
2343 boost::weak_ptr<Connection> m_connection;
2349 * When the callback is invoked, we always abort the current
2350 * transmission. If it is invoked while we are not in the wait()
2351 * of this transport, then we remember that in m_eventTriggered
2352 * and return from wait() right away. The main loop is only
2353 * quit when the transport is waiting in it. This is a precaution
2354 * to not interfere with other parts of the code.
2356 TransportCallback m_callback;
2357 void *m_callbackData;
2358 int m_callbackInterval;
2359 GLibEvent m_eventSource;
2360 bool m_eventTriggered;
2363 SharedBuffer m_incomingMsg;
2364 std::string m_incomingMsgType;
2366 void doWait(boost::shared_ptr<Connection> &connection);
2367 static gboolean timeoutCallback(gpointer transport);
2370 DBusTransportAgent(GMainLoop *loop,
2372 boost::weak_ptr<Connection> connection);
2373 ~DBusTransportAgent();
2375 virtual void setURL(const std::string &url) { m_url = url; }
2376 virtual void setContentType(const std::string &type) { m_type = type; }
2377 virtual void send(const char *data, size_t len);
2378 virtual void cancel() {}
2379 virtual void shutdown();
2380 virtual Status wait(bool noReply = false);
2381 virtual void setCallback (TransportCallback cb, void * udata, int interval)
2384 m_callbackData = udata;
2385 m_callbackInterval = interval;
2388 virtual void getReply(const char *&data, size_t &len, std::string &contentType);
2392 * A wrapper for handling info request and response.
2396 typedef std::map<string, string> InfoMap;
2398 // status of current request
2400 ST_RUN, // request is running
2401 ST_OK, // ok, response is gotten
2402 ST_TIMEOUT, // timeout
2403 ST_CANCEL // request is cancelled
2408 * The default timeout is 120 seconds
2410 InfoReq(DBusServer &server,
2412 const InfoMap ¶meters,
2413 const Session *session,
2414 uint32_t timeout = 120);
2419 * check whether the request is ready. Also give an opportunity
2420 * to poll the sources and then check the response is ready
2421 * @return the state of the request
2426 * wait the response until timeout, abort or suspend. It may be blocked.
2427 * The response is returned though the parameter 'response' when the Status is
2428 * 'ST_OK'. Otherwise, corresponding statuses are returned.
2429 * @param response the received response if gotten
2430 * @param interval the interval to check abort, suspend and timeout, in seconds
2431 * @return the current status
2433 Status wait(InfoMap &response, uint32_t interval = 3);
2436 * get response when it is ready. If false, nothing will be set in response
2438 bool getResponse(InfoMap &response);
2440 /** cancel the request. If request is done, cancel won't do anything */
2443 /** get current status in string format */
2444 string getStatusStr() const { return statusToString(m_status); }
2447 static string statusToString(Status status);
2455 static string infoStateToString(InfoState state);
2457 /** callback for the timemout source */
2458 static gboolean checkCallback(gpointer data);
2460 /** check whether the request is timeout */
2461 bool checkTimeout();
2463 friend class DBusServer;
2465 /** set response from dbus clients */
2466 void setResponse(const Caller_t &caller, const string &state, const InfoMap &response);
2468 /** send 'done' state if needed */
2471 string getId() const { return m_id; }
2472 string getSessionPath() const { return m_session ? m_session->getPath() : ""; }
2473 string getInfoStateStr() const { return infoStateToString(m_infoState); }
2474 string getHandler() const { return m_handler; }
2475 string getType() const { return m_type; }
2476 const InfoMap& getParam() const { return m_param; }
2478 DBusServer &m_server;
2480 /** caller's session, might be NULL */
2481 const Session *m_session;
2483 /** unique id of this info request */
2486 /** info req state defined in dbus api */
2487 InfoState m_infoState;
2489 /** status to indicate the info request is timeout, ok, abort, etc */
2492 /** the handler of the responsed dbus client */
2495 /** the type of the info request */
2498 /** parameters from info request callers */
2501 /** response returned from dbus clients */
2504 /** default timeout is 120 seconds */
2512 /***************** Client implementation ****************/
2516 SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s is destructing", m_ID.c_str());
2518 // explicitly detach all resources instead of just freeing the
2519 // list, so that the special behavior for sessions in detach() is
2521 while (!m_resources.empty()) {
2522 detach(m_resources.front().get());
2526 bool Client::sessionExpired(const boost::shared_ptr<Session> &session)
2528 SE_LOG_DEBUG(NULL, NULL, "session %s expired",
2529 session->getSessionID().c_str());
2530 // don't call me again
2534 void Client::detach(Resource *resource)
2536 for (Resources_t::iterator it = m_resources.begin();
2537 it != m_resources.end();
2539 if (it->get() == resource) {
2541 boost::shared_ptr<Session> session = boost::dynamic_pointer_cast<Session>(*it);
2543 // Special behavior for sessions: keep them
2544 // around for another minute after the last
2545 // client detaches. This allows another client
2546 // to attach and/or get information about the
2548 // This is implemented as a timeout which holds
2549 // a reference to the session. Once the timeout
2550 // fires, it is called and then removed, which
2551 // removes the reference.
2552 m_server.addTimeout(boost::bind(&Client::sessionExpired,
2556 // allow other sessions to start
2560 // this will trigger removal of the resource if
2561 // the client was the last remaining owner
2562 m_resources.erase(it);
2567 SE_THROW_EXCEPTION(InvalidCall, "cannot detach from resource that client is not attached to");
2571 /***************** ReadOperations implementation ****************/
2573 ReadOperations::ReadOperations(const std::string &config_name, DBusServer &server) :
2574 m_configName(config_name), m_server(server)
2577 void ReadOperations::getConfigs(bool getTemplates, std::vector<std::string> &configNames)
2580 // get device list from dbus server, currently only bluetooth devices
2581 SyncConfig::DeviceList devices;
2582 m_server.getDeviceList(devices);
2584 //clear existing templates in dbus server
2585 m_server.clearPeerTempls();
2587 SyncConfig::TemplateList list = SyncConfig::getPeerTemplates(devices);
2588 std::map<std::string, int> numbers;
2589 BOOST_FOREACH(const boost::shared_ptr<SyncConfig::TemplateDescription> peer, list) {
2590 //if it is not a template for device
2591 if(peer->m_fingerprint.empty()) {
2592 configNames.push_back(peer->m_templateId);
2594 string templName = "Bluetooth_";
2595 templName += peer->m_deviceId;
2597 std::map<std::string, int>::iterator it = numbers.find(peer->m_deviceId);
2598 if(it == numbers.end()) {
2599 numbers.insert(std::make_pair(peer->m_deviceId, 1));
2605 templName += seq.str();
2607 configNames.push_back(templName);
2608 m_server.addPeerTempl(templName, peer);
2612 SyncConfig::ConfigList list = SyncConfig::getConfigs();
2613 BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server, list) {
2614 configNames.push_back(server.first);
2619 boost::shared_ptr<DBusUserInterface> ReadOperations::getLocalConfig(const string &configName, bool mustExist)
2621 string peer, context;
2622 SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(configName),
2625 boost::shared_ptr<DBusUserInterface> syncConfig(new DBusUserInterface(configName));
2627 /** if config was not set temporarily */
2628 if (!setFilters(*syncConfig)) {
2629 // the default configuration can always be opened for reading,
2630 // everything else must exist
2631 if ((context != "default" || peer != "") &&
2633 !syncConfig->exists()) {
2634 SE_THROW_EXCEPTION(NoSuchConfig, "No configuration '" + configName + "' found");
2640 void ReadOperations::getConfig(bool getTemplate,
2643 map<string, string> localConfigs;
2644 boost::shared_ptr<SyncConfig> dbusConfig;
2645 boost::shared_ptr<DBusUserInterface> dbusUI;
2646 SyncConfig *syncConfig;
2648 /** get server template */
2650 string peer, context;
2652 boost::shared_ptr<SyncConfig::TemplateDescription> peerTemplate =
2653 m_server.getPeerTempl(m_configName);
2655 SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(peerTemplate->m_templateId),
2657 dbusConfig = SyncConfig::createPeerTemplate(peerTemplate->m_path);
2658 // if we have cached template information, add match information for it
2659 localConfigs.insert(pair<string, string>("description", peerTemplate->m_description));
2662 score << peerTemplate->m_rank;
2663 localConfigs.insert(pair<string, string>("score", score.str()));
2664 // Actually this fingerprint is transferred by getConfigs, which refers to device name
2665 localConfigs.insert(pair<string, string>("deviceName", peerTemplate->m_fingerprint));
2666 // This is the fingerprint of the template
2667 localConfigs.insert(pair<string, string>("fingerPrint", peerTemplate->m_matchedModel));
2668 // This is the template name presented to UI (or device class)
2669 if (!peerTemplate->m_templateName.empty()) {
2670 localConfigs.insert(pair<string,string>("templateName", peerTemplate->m_templateName));
2673 // if the peer is client, then replace syncURL with bluetooth
2675 syncURL = "obex-bt://";
2676 syncURL += peerTemplate->m_deviceId;
2678 SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
2680 dbusConfig = SyncConfig::createPeerTemplate(peer);
2683 if(!dbusConfig.get()) {
2684 SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + m_configName + "' found");
2687 // use the shared properties from the right context as filter
2688 // so that the returned template preserves existing properties
2689 boost::shared_ptr<DBusUserInterface> shared = getLocalConfig(string("@") + context, false);
2692 shared->getProperties()->readProperties(props);
2693 dbusConfig->setConfigFilter(true, "", props);
2694 BOOST_FOREACH(std::string source, shared->getSyncSources()) {
2695 SyncSourceNodes nodes = shared->getSyncSourceNodes(source, "");
2697 nodes.getProperties()->readProperties(props);
2698 // Special case "type" property: the value in the context
2699 // is not preserved. Every new peer must ensure that
2700 // its own value is compatible (= same backend) with
2702 props.erase("type");
2703 dbusConfig->setConfigFilter(false, source, props);
2705 syncConfig = dbusConfig.get();
2707 dbusUI = getLocalConfig(m_configName);
2708 //try to check password and read password from gnome keyring if possible
2709 ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
2710 BOOST_FOREACH(const ConfigProperty *prop, registry) {
2711 prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties());
2713 list<string> configuredSources = dbusUI->getSyncSources();
2714 BOOST_FOREACH(const string &sourceName, configuredSources) {
2715 ConfigPropertyRegistry& registry = SyncSourceConfig::getRegistry();
2716 SyncSourceNodes sourceNodes = dbusUI->getSyncSourceNodes(sourceName);
2718 BOOST_FOREACH(const ConfigProperty *prop, registry) {
2719 prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties(),
2720 sourceName, sourceNodes.getProperties());
2723 syncConfig = dbusUI.get();
2726 /** get sync properties and their values */
2727 ConfigPropertyRegistry &syncRegistry = SyncConfig::getRegistry();
2728 BOOST_FOREACH(const ConfigProperty *prop, syncRegistry) {
2729 bool isDefault = false;
2730 string value = prop->getProperty(*syncConfig->getProperties(), &isDefault);
2731 if(boost::iequals(prop->getName(), "syncURL") && !syncURL.empty() ) {
2732 localConfigs.insert(pair<string, string>(prop->getName(), syncURL));
2733 } else if(!isDefault) {
2734 localConfigs.insert(pair<string, string>(prop->getName(), value));
2738 // insert 'configName' of the chosen config (m_configName is not normalized)
2739 localConfigs.insert(pair<string, string>("configName", syncConfig->getConfigName()));
2741 config.insert(pair<string,map<string, string> >("", localConfigs));
2743 /* get configurations from sources */
2744 list<string> sources = syncConfig->getSyncSources();
2745 BOOST_FOREACH(const string &name, sources) {
2746 localConfigs.clear();
2747 SyncSourceNodes sourceNodes = syncConfig->getSyncSourceNodes(name);
2748 ConfigPropertyRegistry &sourceRegistry = SyncSourceConfig::getRegistry();
2749 BOOST_FOREACH(const ConfigProperty *prop, sourceRegistry) {
2750 bool isDefault = false;
2751 string value = prop->getProperty(*sourceNodes.getProperties(), &isDefault);
2753 localConfigs.insert(pair<string, string>(prop->getName(), value));
2756 config.insert(pair<string, map<string, string> >( "source/" + name, localConfigs));
2760 void ReadOperations::getReports(uint32_t start, uint32_t count,
2763 SyncContext client(m_configName, false);
2764 std::vector<string> dirs;
2765 client.getSessions(dirs);
2768 // newest report firstly
2769 for( int i = dirs.size() - 1; i >= 0; --i) {
2770 /** if start plus count is bigger than actual size, then return actual - size reports */
2771 if(index >= start && index - start < count) {
2772 const string &dir = dirs[i];
2773 std::map<string, string> aReport;
2774 // insert a 'dir' as an ID for the current report
2775 aReport.insert(pair<string, string>("dir", dir));
2777 // peerName is also extracted from the dir
2778 string peerName = client.readSessionInfo(dir,report);
2779 boost::shared_ptr<SyncConfig> config(new SyncConfig(m_configName));
2780 string storedPeerName = config->getPeerName();
2781 //if can't find peer name, use the peer name from the log dir
2782 if(!storedPeerName.empty()) {
2783 peerName = storedPeerName;
2786 /** serialize report to ConfigProps and then copy them to reports */
2787 HashFileConfigNode node("/dev/null","",true);
2790 node.readProperties(props);
2792 BOOST_FOREACH(const ConfigProps::value_type &entry, props) {
2793 aReport.insert(entry);
2795 // a new key-value pair <"peer", [peer name]> is transferred
2796 aReport.insert(pair<string, string>("peer", peerName));
2797 reports.push_back(aReport);
2803 void ReadOperations::checkSource(const std::string &sourceName)
2805 boost::shared_ptr<SyncConfig> config(new SyncConfig(m_configName));
2806 setFilters(*config);
2808 list<std::string> sourceNames = config->getSyncSources();
2809 list<std::string>::iterator it;
2810 for(it = sourceNames.begin(); it != sourceNames.end(); ++it) {
2811 if(*it == sourceName) {
2815 if(it == sourceNames.end()) {
2816 SE_THROW_EXCEPTION(NoSuchSource, "'" + m_configName + "' has no '" + sourceName + "' source");
2818 bool checked = false;
2820 // this can already throw exceptions when the config is invalid
2821 SyncSourceParams params(sourceName, config->getSyncSourceNodes(sourceName));
2822 auto_ptr<SyncSource> syncSource(SyncSource::createSource(params, false, config.get()));
2824 if (syncSource.get()) {
2830 Exception::handle();
2834 SE_THROW_EXCEPTION(SourceUnusable, "The source '" + sourceName + "' is not usable");
2837 void ReadOperations::getDatabases(const string &sourceName, SourceDatabases_t &databases)
2839 boost::shared_ptr<SyncConfig> config(new SyncConfig(m_configName));
2840 setFilters(*config);
2842 SyncSourceParams params(sourceName, config->getSyncSourceNodes(sourceName));
2843 const SourceRegistry ®istry(SyncSource::getSourceRegistry());
2844 BOOST_FOREACH(const RegisterSyncSource *sourceInfo, registry) {
2845 SyncSource *source = sourceInfo->m_create(params);
2848 } else if (source == RegisterSyncSource::InactiveSource) {
2849 SE_THROW_EXCEPTION(NoSuchSource, "'" + m_configName + "' backend of source '" + sourceName + "' is not supported");
2851 auto_ptr<SyncSource> autoSource(source);
2852 databases = autoSource->getDatabases();
2857 SE_THROW_EXCEPTION(NoSuchSource, "'" + m_configName + "' has no '" + sourceName + "' source");
2860 /***************** DBusUserInterface implementation **********************/
2861 DBusUserInterface::DBusUserInterface(const std::string &config):
2862 SyncContext(config, true)
2866 inline const char *passwdStr(const std::string &str)
2868 return str.empty() ? NULL : str.c_str();
2871 string DBusUserInterface::askPassword(const string &passwordName,
2872 const string &descr,
2873 const ConfigPasswordKey &key)
2876 #ifdef USE_GNOME_KEYRING
2877 /** here we use server sync url without protocol prefix and
2878 * user account name as the key in the keyring */
2879 /* It is possible to let CmdlineSyncClient decide which of fields in ConfigPasswordKey it would use
2880 * but currently only use passed key instead */
2881 GnomeKeyringResult result;
2884 result = gnome_keyring_find_network_password_sync(passwdStr(key.user),
2885 passwdStr(key.domain),
2886 passwdStr(key.server),
2887 passwdStr(key.object),
2888 passwdStr(key.protocol),
2889 passwdStr(key.authtype),
2893 /** if find password stored in gnome keyring */
2894 if(result == GNOME_KEYRING_RESULT_OK && list && list->data ) {
2895 GnomeKeyringNetworkPasswordData *key_data;
2896 key_data = (GnomeKeyringNetworkPasswordData*)list->data;
2897 password = key_data->password;
2898 gnome_keyring_network_password_list_free(list);
2902 //if not found, return empty
2906 bool DBusUserInterface::savePassword(const string &passwordName,
2907 const string &password,
2908 const ConfigPasswordKey &key)
2910 #ifdef USE_GNOME_KEYRING
2911 /* It is possible to let CmdlineSyncClient decide which of fields in ConfigPasswordKey it would use
2912 * but currently only use passed key instead */
2914 GnomeKeyringResult result;
2915 // write password to keyring
2916 result = gnome_keyring_set_network_password_sync(NULL,
2917 passwdStr(key.user),
2918 passwdStr(key.domain),
2919 passwdStr(key.server),
2920 passwdStr(key.object),
2921 passwdStr(key.protocol),
2922 passwdStr(key.authtype),
2926 /* if set operation is failed */
2927 if(result != GNOME_KEYRING_RESULT_OK) {
2928 #ifdef GNOME_KEYRING_220
2929 SyncContext::throwError("Try to save " + passwordName + " in gnome-keyring but get an error. " + gnome_keyring_result_to_message(result));
2931 /** if gnome-keyring version is below 2.20, it doesn't support 'gnome_keyring_result_to_message'. */
2933 value << (int)result;
2934 SyncContext::throwError("Try to save " + passwordName + " in gnome-keyring but get an error. The gnome-keyring error code is " + value.str() + ".");
2939 /** if no support of gnome-keyring, don't save anything */
2944 void DBusUserInterface::readStdin(string &content)
2946 throwError("reading stdin in D-Bus server not supported, use --daemon=no in command line");
2949 /***************** DBusSync implementation **********************/
2951 DBusSync::DBusSync(const std::string &config,
2953 DBusUserInterface(config),
2958 boost::shared_ptr<TransportAgent> DBusSync::createTransportAgent()
2960 if (m_session.useConnection()) {
2961 // use the D-Bus Connection to send and receive messages
2962 boost::shared_ptr<TransportAgent> agent(new DBusTransportAgent(m_session.getServer().getLoop(),
2964 m_session.getConnection()));
2965 // We don't know whether we'll run as client or server.
2966 // But we as we cannot resend messages via D-Bus even if running as
2967 // client (API not designed for it), let's use the hard timeout
2968 // from RetryDuration here.
2969 int timeout = getRetryDuration();
2971 agent->setCallback(transport_cb,
2972 reinterpret_cast<void *>(static_cast<uintptr_t>(timeout)),
2977 // no connection, use HTTP via libsoup/GMainLoop
2978 GMainLoop *loop = m_session.getServer().getLoop();
2979 boost::shared_ptr<TransportAgent> agent = SyncContext::createTransportAgent(loop);
2984 void DBusSync::displaySyncProgress(sysync::TProgressEventEnum type,
2985 int32_t extra1, int32_t extra2, int32_t extra3)
2987 SyncContext::displaySyncProgress(type, extra1, extra2, extra3);
2988 m_session.syncProgress(type, extra1, extra2, extra3);
2991 void DBusSync::displaySourceProgress(sysync::TProgressEventEnum type,
2993 int32_t extra1, int32_t extra2, int32_t extra3)
2995 SyncContext::displaySourceProgress(type, source, extra1, extra2, extra3);
2996 m_session.sourceProgress(type, source, extra1, extra2, extra3);
2999 void DBusSync::reportStepCmd(sysync::uInt16 stepCmd)
3002 case sysync::STEPCMD_SENDDATA:
3003 case sysync::STEPCMD_RESENDDATA:
3004 case sysync::STEPCMD_NEEDDATA:
3005 //sending or waiting data
3006 m_session.setStepInfo(true);
3009 // otherwise, processing
3010 m_session.setStepInfo(false);
3015 void DBusSync::syncSuccessStart()
3017 m_session.syncSuccessStart();
3020 bool DBusSync::checkForSuspend()
3022 return m_session.isSuspend() || SyncContext::checkForSuspend();
3025 bool DBusSync::checkForAbort()
3027 return m_session.isAbort() || SyncContext::checkForAbort();
3030 int DBusSync::sleep(int intervals)
3032 time_t start = time(NULL);
3034 g_main_context_iteration(NULL, false);
3035 time_t now = time(NULL);
3036 if (checkForSuspend() || checkForAbort()) {
3037 return (intervals - now + start);
3039 if (intervals - now + start <= 0) {
3040 return intervals - now +start;
3045 string DBusSync::askPassword(const string &passwordName,
3046 const string &descr,
3047 const ConfigPasswordKey &key)
3049 string password = DBusUserInterface::askPassword(passwordName, descr, key);
3051 if(password.empty()) {
3052 password = m_session.askPassword(passwordName, descr, key);
3057 /***************** Session implementation ***********************/
3059 void Session::attach(const Caller_t &caller)
3061 boost::shared_ptr<Client> client(m_server.findClient(caller));
3063 throw runtime_error("unknown client");
3065 boost::shared_ptr<Session> me = m_me.lock();
3067 throw runtime_error("session already deleted?!");
3072 void Session::detach(const Caller_t &caller)
3074 boost::shared_ptr<Client> client(m_server.findClient(caller));
3076 throw runtime_error("unknown client");
3078 client->detach(this);
3081 static void setSyncFilters(const ReadOperations::Config_t &config,FilterConfigNode::ConfigFilter &syncFilter,std::map<std::string, FilterConfigNode::ConfigFilter> &sourceFilters)
3083 ReadOperations::Config_t::const_iterator it;
3084 for (it = config.begin(); it != config.end(); it++) {
3085 map<string, string>::const_iterator sit;
3086 if(it->first.empty()) {
3087 for (sit = it->second.begin(); sit != it->second.end(); sit++) {
3088 syncFilter.insert(*sit);
3091 string name = it->first;
3092 if(name.find("source/") == 0) {
3093 name = name.substr(7); ///> 7 is the length of "source/"
3094 FilterConfigNode::ConfigFilter &sourceFilter = sourceFilters[name];
3095 for (sit = it->second.begin(); sit != it->second.end(); sit++) {
3096 sourceFilter.insert(*sit);
3102 void Session::setConfig(bool update, bool temporary,
3103 const ReadOperations::Config_t &config)
3106 SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
3108 if (m_runOperation != OP_NULL) {
3109 string msg = StringPrintf("%s started, cannot change configuration at this time", runOpToString(m_runOperation).c_str());
3110 SE_THROW_EXCEPTION(InvalidCall, msg);
3112 if (!update && temporary) {
3113 throw std::runtime_error("Clearing existing configuration and temporary configuration changes which only affects the duration of the session are mutually exclusive");
3116 m_server.getPresenceStatus().updateConfigPeers (m_configName, config);
3117 /** check whether we need remove the entire configuration */
3118 if(!update && config.empty()) {
3119 boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(getConfigName()));
3120 if(syncConfig.get()) {
3121 syncConfig->remove();
3127 /* save temporary configs in session filters */
3128 setSyncFilters(config, m_syncFilter, m_sourceFilters);
3129 m_tempConfig = true;
3131 FilterConfigNode::ConfigFilter syncFilter;
3132 std::map<std::string, FilterConfigNode::ConfigFilter> sourceFilters;
3133 setSyncFilters(config, syncFilter, sourceFilters);
3134 /* need to save configurations */
3135 boost::shared_ptr<SyncConfig> from(new SyncConfig(getConfigName()));
3136 /* if it is not clear mode and config does not exist, an error throws */
3137 if(update && !from->exists()) {
3138 SE_THROW_EXCEPTION(NoSuchConfig, "The configuration '" + getConfigName() + "' doesn't exist" );
3141 list<string> sources = from->getSyncSources();
3142 list<string>::iterator it;
3143 for(it = sources.begin(); it != sources.end(); ++it) {
3144 string source = "source/";
3146 ReadOperations::Config_t::const_iterator configIt = config.find(source);
3147 if(configIt == config.end()) {
3148 /** if no config for this source, we remove it */
3149 from->removeSyncSource(*it);
3151 /** just clear visiable properties, remove them and their values */
3152 from->clearSyncSourceProperties(*it);
3155 from->clearSyncProperties();
3157 /** generate new sources in the config map */
3158 for (ReadOperations::Config_t::const_iterator it = config.begin(); it != config.end(); ++it) {
3159 string sourceName = it->first;
3160 if(sourceName.find("source/") == 0) {
3161 sourceName = sourceName.substr(7); ///> 7 is the length of "source/"
3162 from->getSyncSourceNodes(sourceName);
3165 /* apply user settings */
3166 from->setConfigFilter(true, "", syncFilter);
3167 map<string, FilterConfigNode::ConfigFilter>::iterator it;
3168 for ( it = sourceFilters.begin(); it != sourceFilters.end(); it++ ) {
3169 from->setConfigFilter(false, it->first, it->second);
3171 boost::shared_ptr<DBusSync> syncConfig(new DBusSync(getConfigName(), *this));
3172 syncConfig->copy(*from, NULL);
3174 syncConfig->preFlush(*syncConfig);
3175 syncConfig->flush();
3180 void Session::initServer(SharedBuffer data, const std::string &messageType)
3182 m_serverMode = true;
3183 m_initialMessage = data;
3184 m_initialMessageType = messageType;
3187 void Session::sync(const std::string &mode, const SourceModes_t &source_modes)
3190 SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
3192 if (m_runOperation == OP_SYNC) {
3193 string msg = StringPrintf("%s started, cannot start again", runOpToString(m_runOperation).c_str());
3194 SE_THROW_EXCEPTION(InvalidCall, msg);
3195 } else if (m_runOperation != OP_NULL) {
3196 string msg = StringPrintf("%s started, cannot start sync", runOpToString(m_runOperation).c_str());
3197 SE_THROW_EXCEPTION(InvalidCall, msg);
3200 m_sync.reset(new DBusSync(getConfigName(), *this));
3202 m_sync->initServer(m_sessionID,
3204 m_initialMessageType);
3205 boost::shared_ptr<Connection> c = m_connection.lock();
3206 if (c && !c->mustAuthenticate()) {
3207 // unsetting username/password disables checking them
3208 m_syncFilter["password"] = "";
3209 m_syncFilter["username"] = "";
3213 if (m_remoteInitiated) {
3214 m_sync->setRemoteInitiated (true);
3217 // Apply temporary config filters. The parameters of this function
3218 // override the source filters, if set.
3219 m_sync->setConfigFilter(true, "", m_syncFilter);
3220 FilterConfigNode::ConfigFilter filter;
3221 filter = m_sourceFilter;
3222 if (!mode.empty()) {
3223 filter[SyncSourceConfig::m_sourcePropSync.getName()] = mode;
3225 m_sync->setConfigFilter(false, "", filter);
3226 BOOST_FOREACH(const std::string &source,
3227 m_sync->getSyncSources()) {
3228 filter = m_sourceFilters[source];
3229 SourceModes_t::const_iterator it = source_modes.find(source);
3230 if (it != source_modes.end()) {
3231 filter[SyncSourceConfig::m_sourcePropSync.getName()] = it->second;
3233 m_sync->setConfigFilter(false, source, filter);
3236 // Update status and progress. From now on, all configured sources
3237 // have their default entry (referencing them by name creates the
3239 BOOST_FOREACH(const std::string source,
3240 m_sync->getSyncSources()) {
3241 m_sourceStatus[source];
3242 m_sourceProgress[source];
3246 m_runOperation = OP_SYNC;
3248 // now that we have a DBusSync object, return from the main loop
3249 // and once that is done, transfer control to that object
3250 g_main_loop_quit(loop);
3253 void Session::abort()
3255 if (m_runOperation != OP_SYNC && m_runOperation != OP_CMDLINE) {
3256 SE_THROW_EXCEPTION(InvalidCall, "sync not started, cannot abort at this time");
3258 m_syncStatus = SYNC_ABORT;
3261 // state change, return to caller so that it can react
3262 g_main_loop_quit(m_server.getLoop());
3265 void Session::suspend()
3267 if (m_runOperation != OP_SYNC && m_runOperation != OP_CMDLINE) {
3268 SE_THROW_EXCEPTION(InvalidCall, "sync not started, cannot suspend at this time");
3270 m_syncStatus = SYNC_SUSPEND;
3272 g_main_loop_quit(m_server.getLoop());
3275 void Session::getStatus(std::string &status,
3277 SourceStatuses_t &sources)
3279 status = syncStatusToString(m_syncStatus);
3280 if (m_stepIsWaiting) {
3281 status += ";waiting";
3285 sources = m_sourceStatus;
3288 void Session::getProgress(int32_t &progress,
3289 SourceProgresses_t &sources)
3291 progress = m_progress;
3292 sources = m_sourceProgress;
3295 void Session::fireStatus(bool flush)
3299 SourceStatuses_t sources;
3301 /** not force flushing and not timeout, return */
3302 if(!flush && !m_statusTimer.timeout()) {
3305 m_statusTimer.reset();
3307 getStatus(status, error, sources);
3308 emitStatus(status, error, sources);
3311 void Session::fireProgress(bool flush)
3314 SourceProgresses_t sources;
3316 /** not force flushing and not timeout, return */
3317 if(!flush && !m_progressTimer.timeout()) {
3320 m_progressTimer.reset();
3322 getProgress(progress, sources);
3323 emitProgress(progress, sources);
3325 string Session::syncStatusToString(SyncStatus state)
3337 return "suspending";
3345 boost::shared_ptr<Session> Session::createSession(DBusServer &server,
3346 const std::string &peerDeviceID,
3347 const std::string &config_name,
3348 const std::string &session,
3349 const std::vector<std::string> &flags)
3351 boost::shared_ptr<Session> me(new Session(server, peerDeviceID, config_name, session, flags));
3356 Session::Session(DBusServer &server,
3357 const std::string &peerDeviceID,
3358 const std::string &config_name,
3359 const std::string &session,
3360 const std::vector<std::string> &flags) :
3361 DBusObjectHelper(server.getConnection(),
3362 std::string("/org/syncevolution/Session/") + session,
3363 "org.syncevolution.Session",
3364 boost::bind(&DBusServer::autoTermCallback, &server)),
3365 ReadOperations(config_name, server),
3368 m_sessionID(session),
3369 m_peerDeviceID(peerDeviceID),
3370 m_serverMode(false),
3371 m_useConnection(false),
3372 m_tempConfig(false),
3376 m_remoteInitiated(false),
3377 m_syncStatus(SYNC_QUEUEING),
3378 m_stepIsWaiting(false),
3379 m_priority(PRI_DEFAULT),
3381 m_progData(m_progress),
3384 m_progressTimer(50),
3385 m_restoreBefore(true),
3386 m_restoreSrcTotal(0),
3388 m_runOperation(OP_NULL),
3390 emitStatus(*this, "StatusChanged"),
3391 emitProgress(*this, "ProgressChanged")
3393 add(this, &Session::attach, "Attach");
3394 add(this, &Session::detach, "Detach");
3395 add(this, &Session::getFlags, "GetFlags");
3396 add(this, &Session::getNormalConfigName, "GetConfigName");
3397 add(static_cast<ReadOperations *>(this), &ReadOperations::getConfigs, "GetConfigs");
3398 add(static_cast<ReadOperations *>(this), &ReadOperations::getConfig, "GetConfig");
3399 add(this, &Session::setConfig, "SetConfig");
3400 add(static_cast<ReadOperations *>(this), &ReadOperations::getReports, "GetReports");
3401 add(static_cast<ReadOperations *>(this), &ReadOperations::checkSource, "CheckSource");
3402 add(static_cast<ReadOperations *>(this), &ReadOperations::getDatabases, "GetDatabases");
3403 add(this, &Session::sync, "Sync");
3404 add(this, &Session::abort, "Abort");
3405 add(this, &Session::suspend, "Suspend");
3406 add(this, &Session::getStatus, "GetStatus");
3407 add(this, &Session::getProgress, "GetProgress");
3408 add(this, &Session::restore, "Restore");
3409 add(this, &Session::checkPresence, "checkPresence");
3410 add(this, &Session::execute, "Execute");
3415 void Session::done()
3421 /* update auto sync manager when a config is changed */
3423 m_server.getAutoSyncManager().update(m_configName);
3425 m_server.dequeue(this);
3427 // now tell other clients about config change?
3429 m_server.configChanged();
3432 // typically set by m_server.dequeue(), but let's really make sure...
3443 void Session::setActive(bool active)
3447 if (m_syncStatus == SYNC_QUEUEING) {
3448 m_syncStatus = SYNC_IDLE;
3452 boost::shared_ptr<Connection> c = m_connection.lock();
3459 void Session::syncProgress(sysync::TProgressEventEnum type,
3460 int32_t extra1, int32_t extra2, int32_t extra3)
3463 case sysync::PEV_SESSIONSTART:
3464 m_progData.setStep(ProgressData::PRO_SYNC_INIT);
3467 case sysync::PEV_SESSIONEND:
3468 if((uint32_t)extra1 != m_error) {
3472 m_progData.setStep(ProgressData::PRO_SYNC_INVALID);
3475 case sysync::PEV_SENDSTART:
3476 m_progData.sendStart();
3478 case sysync::PEV_SENDEND:
3479 case sysync::PEV_RECVSTART:
3480 case sysync::PEV_RECVEND:
3481 m_progData.receiveEnd();
3484 case sysync::PEV_DISPLAY100:
3485 case sysync::PEV_SUSPENDCHECK:
3486 case sysync::PEV_DELETING:
3488 case sysync::PEV_SUSPENDING:
3489 m_syncStatus = SYNC_SUSPEND;
3497 void Session::sourceProgress(sysync::TProgressEventEnum type,
3499 int32_t extra1, int32_t extra2, int32_t extra3)
3501 switch(m_runOperation) {
3503 SourceProgress &progress = m_sourceProgress[source.getName()];
3504 SourceStatus &status = m_sourceStatus[source.getName()];
3506 case sysync::PEV_SYNCSTART:
3507 if(source.getFinalSyncMode() != SYNC_NONE) {
3508 m_progData.setStep(ProgressData::PRO_SYNC_UNINIT);
3512 case sysync::PEV_SYNCEND:
3513 if(source.getFinalSyncMode() != SYNC_NONE) {
3514 status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "done", extra1);
3518 case sysync::PEV_PREPARING:
3519 if(source.getFinalSyncMode() != SYNC_NONE) {
3520 progress.m_phase = "preparing";
3521 progress.m_prepareCount = extra1;
3522 progress.m_prepareTotal = extra2;
3523 m_progData.itemPrepare();
3527 case sysync::PEV_ITEMSENT:
3528 if(source.getFinalSyncMode() != SYNC_NONE) {
3529 progress.m_phase = "sending";
3530 progress.m_sendCount = extra1;
3531 progress.m_sendTotal = extra2;
3535 case sysync::PEV_ITEMRECEIVED:
3536 if(source.getFinalSyncMode() != SYNC_NONE) {
3537 progress.m_phase = "receiving";
3538 progress.m_receiveCount = extra1;
3539 progress.m_receiveTotal = extra2;
3540 m_progData.itemReceive(source.getName(), extra1, extra2);
3544 case sysync::PEV_ALERTED:
3545 if(source.getFinalSyncMode() != SYNC_NONE) {
3546 status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "running", 0);
3548 m_progData.setStep(ProgressData::PRO_SYNC_DATA);
3549 m_progData.addSyncMode(source.getFinalSyncMode());
3560 case sysync::PEV_ALERTED:
3561 // count the total number of sources to be restored
3562 m_restoreSrcTotal++;
3564 case sysync::PEV_SYNCSTART: {
3565 if (source.getFinalSyncMode() != SYNC_NONE) {
3566 SourceStatus &status = m_sourceStatus[source.getName()];
3567 // set statuses as 'restore-from-backup'
3568 status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "running", 0);
3573 case sysync::PEV_SYNCEND: {
3574 if (source.getFinalSyncMode() != SYNC_NONE) {
3576 SourceStatus &status = m_sourceStatus[source.getName()];
3577 status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "done", 0);
3578 m_progress = 100 * m_restoreSrcEnd / m_restoreSrcTotal;
3596 if (m_runOperation != OP_NULL) {
3598 m_syncStatus = SYNC_RUNNING;
3600 switch(m_runOperation) {
3602 SyncMLStatus status;
3603 m_progData.setStep(ProgressData::PRO_SYNC_PREPARE);
3605 status = m_sync->sync();
3607 status = m_sync->handleException();
3612 // if there is a connection, then it is no longer needed
3613 boost::shared_ptr<Connection> c = m_connection.lock();
3617 // report 'sync done' event to listener
3619 m_listener->syncDone(status);
3624 m_sync->restore(m_restoreDir,
3625 m_restoreBefore ? SyncContext::DATABASE_BEFORE_SYNC : SyncContext::DATABASE_AFTER_SYNC);
3631 SyncMLStatus status = Exception::handle();
3636 m_setConfig = m_cmdline->configWasModified();
3642 // we must enter SYNC_DONE under all circumstances,
3643 // even when failing during connection shutdown
3644 m_syncStatus = SYNC_DONE;
3645 m_stepIsWaiting = false;
3649 m_syncStatus = SYNC_DONE;
3650 m_stepIsWaiting = false;
3655 bool Session::setFilters(SyncConfig &config)
3657 /** apply temporary configs to config */
3658 config.setConfigFilter(true, "", m_syncFilter);
3659 // set all sources in the filter to config
3660 BOOST_FOREACH(const SourceFilters_t::value_type &value, m_sourceFilters) {
3661 config.setConfigFilter(false, value.first, value.second);
3663 return m_tempConfig;
3666 void Session::setStepInfo(bool isWaiting)
3668 // if stepInfo doesn't change, then ignore it to avoid duplicate status info
3669 if(m_stepIsWaiting != isWaiting) {
3670 m_stepIsWaiting = isWaiting;
3675 void Session::restore(const string &dir, bool before, const std::vector<std::string> &sources)
3678 SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
3680 if (m_runOperation == OP_RESTORE) {
3681 string msg = StringPrintf("restore started, cannot restore again");
3682 SE_THROW_EXCEPTION(InvalidCall, msg);
3683 } else if (m_runOperation != OP_NULL) {
3684 // actually this never happen currently, for during the real restore process,
3685 // it never poll the sources in default main context
3686 string msg = StringPrintf("%s started, cannot restore", runOpToString(m_runOperation).c_str());
3687 SE_THROW_EXCEPTION(InvalidCall, msg);
3690 m_sync.reset(new DBusSync(getConfigName(), *this));
3692 if(!sources.empty()) {
3693 BOOST_FOREACH(const std::string &source, sources) {
3694 FilterConfigNode::ConfigFilter filter;
3695 filter[SyncSourceConfig::m_sourcePropSync.getName()] = "two-way";
3696 m_sync->setConfigFilter(false, source, filter);
3698 // disable other sources
3699 FilterConfigNode::ConfigFilter disabled;
3700 disabled[SyncSourceConfig::m_sourcePropSync.getName()] = "disabled";
3701 m_sync->setConfigFilter(false, "", disabled);
3703 m_restoreBefore = before;
3705 m_runOperation = OP_RESTORE;
3707 // initiate status and progress and sourceProgress is not calculated currently
3708 BOOST_FOREACH(const std::string source,
3709 m_sync->getSyncSources()) {
3710 m_sourceStatus[source];
3715 g_main_loop_quit(loop);
3718 string Session::runOpToString(RunOperation op)
3732 void Session::execute(const vector<string> &args, const map<string, string> &vars)
3735 SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
3737 if (m_runOperation == OP_CMDLINE) {
3738 SE_THROW_EXCEPTION(InvalidCall, "cmdline started, cannot start again");
3739 } else if (m_runOperation != OP_NULL) {
3740 string msg = StringPrintf("%s started, cannot start cmdline", runOpToString(m_runOperation).c_str());
3741 SE_THROW_EXCEPTION(InvalidCall, msg);
3743 //create ostream with a specified streambuf
3744 m_cmdline.reset(new CmdlineWrapper(*this, args, vars));
3746 if(!m_cmdline->parse()) {
3748 SE_THROW_EXCEPTION(DBusSyncException, "arguments parsing error");
3751 m_runOperation = OP_CMDLINE;
3752 g_main_loop_quit(loop);
3755 inline void insertPair(std::map<string, string> ¶ms,
3757 const string &value)
3759 if(!value.empty()) {
3760 params.insert(pair<string, string>(key, value));
3764 string Session::askPassword(const string &passwordName,
3765 const string &descr,
3766 const ConfigPasswordKey &key)
3768 std::map<string, string> params;
3769 insertPair(params, "description", descr);
3770 insertPair(params, "user", key.user);
3771 insertPair(params, "SyncML server", key.server);
3772 insertPair(params, "domain", key.domain);
3773 insertPair(params, "object", key.object);
3774 insertPair(params, "protocol", key.protocol);
3775 insertPair(params, "authtype", key.authtype);
3776 insertPair(params, "port", key.port ? StringPrintf("%u",key.port) : "");
3777 boost::shared_ptr<InfoReq> req = m_server.createInfoReq("password", params, this);
3778 std::map<string, string> response;
3779 if(req->wait(response) == InfoReq::ST_OK) {
3780 std::map<string, string>::iterator it = response.find("password");
3781 if (it == response.end()) {
3782 SE_THROW_EXCEPTION_STATUS(StatusException, "user didn't provide password, abort", SyncMLStatus(sysync::LOCERR_USERABORT));
3788 SE_THROW_EXCEPTION_STATUS(StatusException, "can't get the password from clients. The password request is '" + req->getStatusStr() + "'", STATUS_PASSWORD_TIMEOUT);
3792 /*Implementation of Session.CheckPresence */
3793 void Session::checkPresence (string &status)
3795 vector<string> transport;
3796 m_server.m_presence.checkPresence (m_configName, status, transport);
3799 void Session::syncSuccessStart()
3801 // if listener, report 'sync started' to it
3803 m_listener->syncSuccessStart();
3807 SessionListener* Session::addListener(SessionListener *listener)
3809 SessionListener *old = m_listener;
3810 m_listener = listener;
3814 /************************ ProgressData implementation *****************/
3815 ProgressData::ProgressData(int32_t &progress)
3816 : m_progress(progress),
3817 m_step(PRO_SYNC_INVALID),
3819 m_internalMode(INTERNAL_NONE)
3822 * init default units of each step
3824 float totalUnits = 0.0;
3825 for(int i = 0; i < PRO_SYNC_TOTAL; i++) {
3826 float units = getDefaultUnits((ProgressStep)i);
3827 m_syncUnits[i] = units;
3828 totalUnits += units;
3830 m_propOfUnit = 1.0 / totalUnits;
3833 * init default sync step proportions. each step stores proportions of
3834 * its previous steps and itself.
3837 for(int i = 1; i < PRO_SYNC_TOTAL - 1; i++) {
3838 m_syncProp[i] = m_syncProp[i - 1] + m_syncUnits[i] / totalUnits;
3840 m_syncProp[PRO_SYNC_TOTAL - 1] = 1.0;
3843 void ProgressData::setStep(ProgressStep step)
3845 if(m_step != step) {
3846 /** if state is changed, progress is set as the end of current step*/
3847 m_progress = 100.0 * m_syncProp[(int)m_step];
3848 m_step = step; ///< change to new state
3849 m_sendCounts = 0; ///< clear send/receive counts
3850 m_source = ""; ///< clear source
3854 void ProgressData::sendStart()
3856 checkInternalMode();
3859 /* self adapts. If a new send and not default, we need re-calculate proportions */
3860 if(m_sendCounts > MSG_SEND_RECEIVE_TIMES) {
3861 m_syncUnits[(int)m_step] += 1;
3865 * If in the send operation of PRO_SYNC_UNINIT, it often takes extra time
3866 * to send message due to items handling
3868 if(m_step == PRO_SYNC_UNINIT && m_syncUnits[(int)m_step] != MSG_SEND_RECEIVE_TIMES) {
3869 updateProg(DATA_PREPARE_RATIO);
3873 void ProgressData::receiveEnd()
3876 * often receiveEnd is the last operation of each step by default.
3877 * If more send/receive, then we need expand proportion of current
3878 * step and re-calc them
3880 updateProg(m_syncUnits[(int)m_step]);
3883 void ProgressData::addSyncMode(SyncMode mode)
3888 m_internalMode |= INTERNAL_TWO_WAY;
3890 case SYNC_ONE_WAY_FROM_CLIENT:
3891 case SYNC_REFRESH_FROM_CLIENT:
3892 m_internalMode |= INTERNAL_ONLY_TO_CLIENT;
3894 case SYNC_ONE_WAY_FROM_SERVER:
3895 case SYNC_REFRESH_FROM_SERVER:
3896 m_internalMode |= INTERNAL_ONLY_TO_SERVER;
3903 void ProgressData::itemPrepare()
3905 checkInternalMode();
3907 * only the first PEV_ITEMPREPARE event takes some time
3908 * due to data access, other events don't according to
3911 if(m_source.empty()) {
3912 m_source = "source"; ///< use this to check whether itemPrepare occurs
3913 updateProg(DATA_PREPARE_RATIO);
3917 void ProgressData::itemReceive(const string &source, int count, int total)
3920 * source is used to check whether a new source is received
3921 * If the first source, we compare its total number and default number
3922 * then re-calc sync units
3924 if(m_source.empty()) {
3927 m_syncUnits[PRO_SYNC_UNINIT] += ONEITEM_RECEIVE_RATIO * (total - DEFAULT_ITEMS);
3930 /** if another new source, add them into sync units */
3931 } else if(m_source != source){
3934 m_syncUnits[PRO_SYNC_UNINIT] += ONEITEM_RECEIVE_RATIO * total;
3938 updateProg(ONEITEM_RECEIVE_RATIO);
3941 void ProgressData::updateProg(float ratio)
3943 m_progress += m_propOfUnit * 100 * ratio;
3944 m_syncUnits[(int)m_step] -= ratio;
3947 /** dynamically adapt the proportion of each step by their current units */
3948 void ProgressData::recalc()
3950 float units = getRemainTotalUnits();
3951 if(std::abs(units) < std::numeric_limits<float>::epsilon()) {
3954 m_propOfUnit = ( 100.0 - m_progress ) / (100.0 * units);
3956 if(m_step != PRO_SYNC_TOTAL -1 ) {
3957 m_syncProp[(int)m_step] = m_progress / 100.0 + m_syncUnits[(int)m_step] * m_propOfUnit;
3958 for(int i = ((int)m_step) + 1; i < PRO_SYNC_TOTAL - 1; i++) {
3959 m_syncProp[i] = m_syncProp[i - 1] + m_syncUnits[i] * m_propOfUnit;
3964 void ProgressData::checkInternalMode()
3966 if(!m_internalMode) {
3968 } else if(m_internalMode & INTERNAL_TWO_WAY) {
3970 } else if(m_internalMode & INTERNAL_ONLY_TO_CLIENT) {
3971 // only to client, remove units of prepare and send
3972 m_syncUnits[PRO_SYNC_DATA] -= (ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO);
3974 } else if(m_internalMode & INTERNAL_ONLY_TO_SERVER) {
3975 // only to server, remove units of receive
3976 m_syncUnits[PRO_SYNC_UNINIT] -= (ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO);
3979 m_internalMode = INTERNAL_NONE;
3982 float ProgressData::getRemainTotalUnits()
3985 for(int i = (int)m_step; i < PRO_SYNC_TOTAL; i++) {
3986 total += m_syncUnits[i];
3991 float ProgressData::getDefaultUnits(ProgressStep step)
3994 case PRO_SYNC_PREPARE:
3995 return PRO_SYNC_PREPARE_RATIO;
3997 return CONN_SETUP_RATIO + MSG_SEND_RECEIVE_TIMES;
3999 return ONEITEM_SEND_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO + MSG_SEND_RECEIVE_TIMES;
4000 case PRO_SYNC_UNINIT:
4001 return ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO + MSG_SEND_RECEIVE_TIMES;
4007 /************************ Connection implementation *****************/
4009 void Connection::failed(const std::string &reason)
4011 if (m_failure.empty()) {
4014 m_session->setConnectionError(reason);
4017 if (m_state != FAILED) {
4023 std::string Connection::buildDescription(const StringMap &peer)
4025 StringMap::const_iterator
4026 desc = peer.find("description"),
4027 id = peer.find("id"),
4028 trans = peer.find("transport"),
4029 trans_desc = peer.find("transport_description");
4031 buffer.reserve(256);
4032 if (desc != peer.end()) {
4033 buffer += desc->second;
4035 if (id != peer.end() || trans != peer.end()) {
4036 if (!buffer.empty()) {
4040 if (id != peer.end()) {
4041 buffer += id->second;
4042 if (trans != peer.end()) {
4046 if (trans != peer.end()) {
4047 buffer += trans->second;
4048 if (trans_desc != peer.end()) {
4050 buffer += trans_desc->second;
4058 void Connection::wakeupSession()
4061 g_main_loop_quit(m_loop);
4066 void Connection::process(const Caller_t &caller,
4067 const std::pair<size_t, const uint8_t *> &message,
4068 const std::string &message_type)
4070 SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s sends %lu bytes via connection %s, %s",
4072 (unsigned long)message.first,
4074 message_type.c_str());
4076 boost::shared_ptr<Client> client(m_server.findClient(caller));
4078 throw runtime_error("unknown client");
4081 boost::shared_ptr<Connection> myself =
4082 boost::static_pointer_cast<Connection, Resource>(client->findResource(this));
4084 throw runtime_error("client does not own connection");
4087 // any kind of error from now on terminates the connection
4092 std::string peerDeviceID;
4093 bool serverMode = false;
4094 // check message type, determine whether we act
4095 // as client or server, choose config
4096 if (message_type == "HTTP Config") {
4097 // type used for testing, payload is config name
4098 config.assign(reinterpret_cast<const char *>(message.second),
4100 } else if (message_type == TransportAgent::m_contentTypeServerAlertedNotificationDS) {
4101 sysync::SanPackage san;
4102 if (san.PassSan(const_cast<uint8_t *>(message.second), message.first, 2) || san.GetHeader()) {
4103 // We are very tolerant regarding the content of the message.
4104 // If it doesn't parse, try to do something useful anyway.
4105 // only for SAN 1.2, for SAN 1.0/1.1 we can not be sure
4106 // whether it is a SAN package or a normal sync pacakge
4107 if (message_type == TransportAgent::m_contentTypeServerAlertedNotificationDS) {
4109 SE_LOG_DEBUG(NULL, NULL, "SAN parsing failed, falling back to 'default' config");
4111 } else { //Server alerted notification case
4112 // Extract server ID and match it against a server
4113 // configuration. Multiple different peers might use the
4114 // same serverID ("PC Suite"), so check properties of the
4115 // of our configs first before going back to the name itself.
4116 std::string serverID = san.fServerID;
4117 SyncConfig::ConfigList servers = SyncConfig::getConfigs();
4118 BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server,
4120 SyncConfig conf(server.first);
4121 vector<string> urls = conf.getSyncURL();
4122 BOOST_FOREACH (const string &url, urls) {
4123 if (url == serverID) {
4124 config = server.first;
4128 if (!config.empty()) {
4133 // for Bluetooth transports match against mac address.
4134 StringMap::const_iterator id = m_peer.find("id"),
4135 trans = m_peer.find("transport");
4136 if (trans != m_peer.end() && id != m_peer.end()) {
4137 if (trans->second == "org.openobex.obexd") {
4138 m_peerBtAddr = id->second.substr(0, id->second.find("+"));
4139 BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server,
4141 SyncConfig conf(server.first);
4142 vector<string> urls = conf.getSyncURL();
4143 BOOST_FOREACH (string &url, urls){
4144 url = url.substr (0, url.find("+"));
4145 SE_LOG_DEBUG (NULL, NULL, "matching against %s",url.c_str());
4146 if (url.find ("obex-bt://") ==0 && url.substr(strlen("obex-bt://"), url.npos) == m_peerBtAddr) {
4147 config = server.first;
4151 if (!config.empty()){
4158 if (config.empty()) {
4159 BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server,
4161 if (server.first == serverID) {
4168 // create a default configuration name if none matched
4169 if (config.empty()) {
4170 config = serverID+"_"+getCurrentTime();
4173 "SAN Server ID '%s' unknown, falling back to automatically created '%s' config",
4174 serverID.c_str(), config.c_str());
4178 SE_LOG_DEBUG(NULL, NULL, "SAN sync with config %s", config.c_str());
4180 m_SANContent.reset (new SANContent ());
4181 // extract number of sources
4182 int numSources = san.fNSync;
4184 uint32_t contentType;
4185 std::string serverURI;
4187 SE_LOG_DEBUG(NULL, NULL, "SAN message with no sources, using selected modes");
4188 // Synchronize all known sources with the default mode.
4189 if (san.GetNthSync(0, syncType, contentType, serverURI)) {
4190 SE_LOG_DEBUG(NULL, NULL, "SAN invalid header, using default modes");
4191 } else if (syncType < SYNC_FIRST || syncType > SYNC_LAST) {
4192 SE_LOG_DEBUG(NULL, NULL, "SAN invalid sync type %d, using default modes", syncType);
4194 m_syncMode = PrettyPrintSyncMode(SyncMode(syncType), true);
4195 SE_LOG_DEBUG(NULL, NULL, "SAN sync mode for all configured sources: %s", m_syncMode.c_str());
4198 for (int sync = 1; sync <= numSources; sync++) {
4199 if (san.GetNthSync(sync, syncType, contentType, serverURI)) {
4200 SE_LOG_DEBUG(NULL, NULL, "SAN invalid sync entry #%d", sync);
4201 } else if (syncType < SYNC_FIRST || syncType > SYNC_LAST) {
4202 SE_LOG_DEBUG(NULL, NULL, "SAN invalid sync type %d for entry #%d, ignoring entry", syncType, sync);
4204 std::string syncMode = PrettyPrintSyncMode(SyncMode(syncType), true);
4205 m_SANContent->m_syncType.push_back (syncMode);
4206 m_SANContent->m_serverURI.push_back (serverURI);
4207 m_SANContent->m_contentType.push_back (contentType);
4212 // TODO: use the session ID set by the server if non-null
4213 } else if (// relaxed checking for XML: ignore stuff like "; CHARSET=UTF-8"
4214 message_type.substr(0, message_type.find(';')) == TransportAgent::m_contentTypeSyncML ||
4215 message_type == TransportAgent::m_contentTypeSyncWBXML) {
4216 // run a new SyncML session as server
4218 if (m_peer.find("config") == m_peer.end() &&
4219 !m_peer["config"].empty()) {
4220 SE_LOG_DEBUG(NULL, NULL, "ignoring pre-chosen config '%s'",
4221 m_peer["config"].c_str());
4224 // peek into the data to extract the locURI = device ID,
4225 // then use it to find the configuration
4226 SyncContext::SyncMLMessageInfo info;
4227 info = SyncContext::analyzeSyncMLMessage(reinterpret_cast<const char *>(message.second),
4230 if (info.m_deviceID.empty()) {
4231 // TODO: proper exception
4232 throw runtime_error("could not extract LocURI=deviceID from initial message");
4234 BOOST_FOREACH(const SyncConfig::ConfigList::value_type &entry,
4235 SyncConfig::getConfigs()) {
4236 SyncConfig peer(entry.first);
4237 if (info.m_deviceID == peer.getRemoteDevID()) {
4238 config = entry.first;
4239 SE_LOG_INFO(NULL, NULL, "matched %s against config %s (%s)",
4240 info.toString().c_str(),
4241 entry.first.c_str(),
4242 entry.second.c_str());
4246 if (config.empty()) {
4247 // TODO: proper exception
4248 throw runtime_error(string("no configuration found for ") +
4252 // abort previous session of this client
4253 m_server.killSessions(info.m_deviceID);
4254 peerDeviceID = info.m_deviceID;
4256 throw runtime_error(StringPrintf("message type '%s' not supported for starting a sync", message_type.c_str()));
4259 // run session as client or server
4260 m_state = PROCESSING;
4261 m_session = Session::createSession(m_server,
4266 m_session->initServer(SharedBuffer(reinterpret_cast<const char *>(message.second),
4270 m_session->setPriority(Session::PRI_CONNECTION);
4271 m_session->setConnection(myself);
4272 // this will be reset only when the connection shuts down okay
4273 // or overwritten with the error given to us in
4274 // Connection::close()
4275 m_session->setConnectionError("closed prematurely");
4276 m_server.enqueue(m_session);
4280 throw std::runtime_error("protocol error: already processing a message");
4283 m_incomingMsg = SharedBuffer(reinterpret_cast<const char *>(message.second),
4285 m_incomingMsgType = message_type;
4286 m_state = PROCESSING;
4287 // get out of DBusTransportAgent::wait()
4292 throw std::runtime_error("protocol error: final reply sent, no further message processing possible");
4294 throw std::runtime_error("protocol error: connection closed, no further message processing possible");
4297 throw std::runtime_error(m_failure);
4300 throw std::runtime_error("protocol error: unknown internal state");
4303 } catch (const std::exception &error) {
4304 failed(error.what());
4307 failed("unknown exception in Connection::process");
4312 void Connection::close(const Caller_t &caller,
4314 const std::string &error)
4316 SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s closes connection %s %s%s%s",
4319 normal ? "normally" : "with error",
4320 error.empty() ? "" : ": ",
4323 boost::shared_ptr<Client> client(m_server.findClient(caller));
4325 throw runtime_error("unknown client");
4330 std::string err = error.empty() ?
4331 "connection closed unexpectedly" :
4334 m_session->setConnectionError(err);
4340 m_session->setConnectionError("");
4344 // remove reference to us from client, will destruct *this*
4346 client->detach(this);
4349 void Connection::abort()
4358 void Connection::shutdown()
4360 // trigger removal of this connection by removing all
4362 m_server.detach(this);
4365 Connection::Connection(DBusServer &server,
4366 const DBusConnectionPtr &conn,
4367 const std::string &sessionID,
4368 const StringMap &peer,
4369 bool must_authenticate) :
4370 DBusObjectHelper(conn.get(),
4371 std::string("/org/syncevolution/Connection/") + sessionID,
4372 "org.syncevolution.Connection",
4373 boost::bind(&DBusServer::autoTermCallback, &server)),
4376 m_mustAuthenticate(must_authenticate),
4378 m_sessionID(sessionID),
4380 sendAbort(*this, "Abort"),
4382 reply(*this, "Reply"),
4383 m_description(buildDescription(peer))
4385 add(this, &Connection::process, "Process");
4386 add(this, &Connection::close, "Close");
4389 m_server.autoTermRef();
4392 Connection::~Connection()
4394 SE_LOG_DEBUG(NULL, NULL, "done with connection to '%s'%s%s%s",
4395 m_description.c_str(),
4396 m_state == DONE ? ", normal shutdown" : " unexpectedly",
4397 m_failure.empty() ? "" : ": ",
4400 if (m_state != DONE) {
4403 // DBusTransportAgent waiting? Wake it up.
4405 m_session.use_count();
4408 // log errors, but do not propagate them because we are
4410 Exception::handle();
4412 m_server.autoTermUnref();
4415 void Connection::ready()
4417 //if configuration not yet created
4418 std::string configName = m_session->getConfigName();
4419 SyncConfig config (configName);
4420 if (!config.exists() && m_SANContent) {
4421 SE_LOG_DEBUG (NULL, NULL, "Configuration %s not exists for a runnable session in a SAN context, create it automatically", configName.c_str());
4422 ReadOperations::Config_t from;
4423 const std::string templateName = "SyncEvolution";
4424 // TODO: support SAN from other well known servers
4425 ReadOperations ops(templateName, m_server);
4426 ops.getConfig(true , from);
4427 if (!m_peerBtAddr.empty()){
4428 from[""]["SyncURL"] = string ("obex-bt://") + m_peerBtAddr;
4430 m_session->setConfig (false, false, from);
4432 const SyncContext context (configName);
4433 std::list<std::string> sources = context.getSyncSources();
4435 if (m_SANContent && !m_SANContent->m_syncType.empty()) {
4436 // check what the server wants us to synchronize
4437 // and only synchronize that
4438 m_syncMode = "disabled";
4439 for (size_t sync=0; sync<m_SANContent->m_syncType.size(); sync++) {
4440 std::string syncMode = m_SANContent->m_syncType[sync];
4441 std::string serverURI = m_SANContent->m_serverURI[sync];
4442 //uint32_t contentType = m_SANContent->m_contentType[sync];
4444 BOOST_FOREACH(const std::string &source, sources) {
4445 boost::shared_ptr<const PersistentSyncSourceConfig> sourceConfig(context.getSyncSourceConfig(source));
4446 // prefix match because the local
4447 // configuration might contain
4448 // additional parameters (like date
4449 // range selection for events)
4450 if (boost::starts_with(sourceConfig->getURI(), serverURI)) {
4451 SE_LOG_DEBUG(NULL, NULL,
4452 "SAN entry #%d = source %s with mode %s",
4453 (int)sync, source.c_str(), syncMode.c_str());
4454 m_sourceModes[source] = syncMode;
4460 SE_LOG_DEBUG(NULL, NULL,
4461 "SAN entry #%d with mode %s ignored because Server URI %s is unknown",
4462 (int)sync, syncMode.c_str(), serverURI.c_str());
4465 if (m_sourceModes.empty()) {
4466 SE_LOG_DEBUG(NULL, NULL,
4467 "SAN message with no known entries, falling back to default");
4473 m_session->setRemoteInitiated(true);
4475 // proceed with sync now that our session is ready
4476 m_session->sync(m_syncMode, m_sourceModes);
4479 /****************** DBusTransportAgent implementation **************/
4481 DBusTransportAgent::DBusTransportAgent(GMainLoop *loop,
4483 boost::weak_ptr<Connection> connection) :
4486 m_connection(connection),
4488 m_eventTriggered(false),
4493 DBusTransportAgent::~DBusTransportAgent()
4495 boost::shared_ptr<Connection> connection = m_connection.lock();
4497 connection->shutdown();
4501 void DBusTransportAgent::send(const char *data, size_t len)
4503 boost::shared_ptr<Connection> connection = m_connection.lock();
4506 SE_THROW_EXCEPTION(TransportException,
4507 "D-Bus peer has disconnected");
4510 if (connection->m_state != Connection::PROCESSING) {
4511 SE_THROW_EXCEPTION(TransportException,
4512 "cannot send to our D-Bus peer");
4515 // Change state in advance. If we fail while replying, then all
4516 // further resends will fail with the error above.
4517 connection->m_state = Connection::WAITING;
4518 connection->m_incomingMsg = SharedBuffer();
4520 // setup regular callback
4522 m_eventSource = g_timeout_add_seconds(m_callbackInterval, timeoutCallback, static_cast<gpointer>(this));
4524 m_eventTriggered = false;
4526 // TODO: turn D-Bus exceptions into transport exceptions
4528 meta["URL"] = m_url;
4529 connection->reply(std::make_pair(len, reinterpret_cast<const uint8_t *>(data)),
4530 m_type, meta, false, connection->m_sessionID);
4533 void DBusTransportAgent::shutdown()
4535 boost::shared_ptr<Connection> connection = m_connection.lock();
4538 SE_THROW_EXCEPTION(TransportException,
4539 "D-Bus peer has disconnected");
4542 if (connection->m_state != Connection::FAILED) {
4543 // send final, empty message and wait for close
4544 connection->m_state = Connection::FINAL;
4545 connection->reply(std::pair<size_t, const uint8_t *>(0, 0),
4547 true, connection->m_sessionID);
4551 gboolean DBusTransportAgent::timeoutCallback(gpointer transport)
4553 DBusTransportAgent *me = static_cast<DBusTransportAgent *>(transport);
4554 me->m_callback(me->m_callbackData);
4555 // TODO: check or remove return code from callback?!
4556 me->m_eventTriggered = true;
4557 if (me->m_waiting) {
4558 g_main_loop_quit(me->m_loop);
4563 void DBusTransportAgent::doWait(boost::shared_ptr<Connection> &connection)
4565 // let Connection wake us up when it has a reply or
4566 // when it closes down
4567 connection->m_loop = m_loop;
4569 // release our reference so that the Connection instance can
4570 // be destructed when requested by the D-Bus peer
4575 g_main_loop_run(m_loop);
4579 DBusTransportAgent::Status DBusTransportAgent::wait(bool noReply)
4581 boost::shared_ptr<Connection> connection = m_connection.lock();
4584 SE_THROW_EXCEPTION(TransportException,
4585 "D-Bus peer has disconnected");
4588 switch (connection->m_state) {
4589 case Connection::PROCESSING:
4590 m_incomingMsg = connection->m_incomingMsg;
4591 m_incomingMsgType = connection->m_incomingMsgType;
4594 case Connection::FINAL:
4595 if (m_eventTriggered) {
4600 // if the connection is still available, then keep waiting
4601 connection = m_connection.lock();
4604 } else if (m_session.getConnectionError().empty()) {
4607 SE_THROW_EXCEPTION(TransportException, m_session.getConnectionError());
4611 case Connection::WAITING:
4613 // message is sent as far as we know, so return
4617 if (m_eventTriggered) {
4622 // tell caller to check again
4625 case Connection::DONE:
4627 SE_THROW_EXCEPTION(TransportException,
4628 "internal error: transport has shut down, can no longer receive reply");
4633 SE_THROW_EXCEPTION(TransportException,
4634 "internal error: send() on connection which is not ready");
4641 void DBusTransportAgent::getReply(const char *&data, size_t &len, std::string &contentType)
4643 data = m_incomingMsg.get();
4644 len = m_incomingMsg.size();
4645 contentType = m_incomingMsgType;
4648 /********************* PresenceStatus implementation ****************/
4649 void PresenceStatus::init(){
4650 //initialize the configured peer list
4652 SyncConfig::ConfigList list = SyncConfig::getConfigs();
4653 BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server, list) {
4654 SyncConfig config (server.first);
4655 vector<string> urls = config.getSyncURL();
4656 m_peers[server.first].clear();
4657 BOOST_FOREACH (const string &url, urls) {
4658 // take current status into account,
4659 // PresenceStatus::checkPresence() calls init() and
4660 // expects up-to-date information
4662 if ((boost::starts_with(url, "obex-bt") && m_btPresence) ||
4663 (boost::starts_with (url, "http") && m_httpPresence)) {
4666 status = NOTRANSPORT;
4668 m_peers[server.first].push_back(make_pair(url, status));
4675 /* Implement DBusServer::checkPresence*/
4676 void PresenceStatus::checkPresence (const string &peer, string& status, std::vector<std::string> &transport) {
4679 //might triggered by updateConfigPeers
4683 string peerName = SyncConfig::normalizeConfigString (peer);
4684 vector< pair<string, PeerStatus> > mytransports = m_peers[peerName];
4685 if (mytransports.empty()) {
4686 //wrong config name?
4687 status = status2string(NOTRANSPORT);
4691 PeerStatus mystatus = MIGHTWORK;
4693 //only if all transports are unavailable can we declare the peer
4694 //status as unavailable
4695 BOOST_FOREACH (PeerStatusPair &mytransport, mytransports) {
4696 if (mytransport.second == MIGHTWORK) {
4697 transport.push_back (mytransport.first);
4700 if (transport.empty()) {
4701 mystatus = NOTRANSPORT;
4703 status = status2string(mystatus);
4706 void PresenceStatus::updateConfigPeers (const std::string &peer, const ReadOperations::Config_t &config) {
4707 ReadOperations::Config_t::const_iterator iter = config.find ("");
4708 if (iter != config.end()) {
4709 //As a simple approach, just reinitialize the whole STATUSMAP
4710 //it will cause later updatePresenceStatus resend all signals
4711 //and a reload in checkPresence
4712 m_initiated = false;
4716 void PresenceStatus::updatePresenceStatus (bool newStatus, PresenceStatus::TransportType type) {
4717 if (type == PresenceStatus::HTTP_TRANSPORT) {
4718 updatePresenceStatus (newStatus, m_btPresence);
4719 } else if (type == PresenceStatus::BT_TRANSPORT) {
4720 updatePresenceStatus (m_httpPresence, newStatus);
4725 void PresenceStatus::updatePresenceStatus (bool httpPresence, bool btPresence) {
4726 bool httpChanged = (m_httpPresence != httpPresence);
4727 bool btChanged = (m_btPresence != btPresence);
4729 m_httpTimer.reset();
4735 if (m_initiated && !httpChanged && !btChanged) {
4740 //initialize the configured peer list using old presence status
4741 bool initiated = m_initiated;
4746 // switch to new status
4747 m_httpPresence = httpPresence;
4748 m_btPresence = btPresence;
4750 //iterate all configured peers and fire singals
4751 BOOST_FOREACH (StatusPair &peer, m_peers) {
4752 //iterate all possible transports
4753 //TODO One peer might got more than one signals, avoid this
4754 std::vector<pair<string, PeerStatus> > &transports = peer.second;
4755 BOOST_FOREACH (PeerStatusPair &entry, transports) {
4756 string url = entry.first;
4757 if (boost::starts_with (url, "http") && (httpChanged || !initiated)) {
4758 entry.second = m_httpPresence ? MIGHTWORK: NOTRANSPORT;
4759 m_server.emitPresence (peer.first, status2string (entry.second), entry.first);
4760 SE_LOG_DEBUG(NULL, NULL,
4761 "http presence signal %s,%s,%s",
4763 status2string (entry.second).c_str(), entry.first.c_str());
4764 } else if (boost::starts_with (url, "obex-bt") && (btChanged || !initiated)) {
4765 entry.second = m_btPresence ? MIGHTWORK: NOTRANSPORT;
4766 m_server.emitPresence (peer.first, status2string (entry.second), entry.first);
4767 SE_LOG_DEBUG(NULL, NULL,
4768 "bluetooth presence signal %s,%s,%s",
4770 status2string (entry.second).c_str(), entry.first.c_str());
4776 /********************** Connman Client implementation **************/
4777 ConnmanClient::ConnmanClient(DBusServer &server):
4779 m_propertyChanged(*this, "PropertyChanged")
4781 const char *connmanTest = getenv ("DBUS_TEST_CONNMAN");
4782 m_connmanConn = b_dbus_setup_bus (connmanTest ? DBUS_BUS_SESSION: DBUS_BUS_SYSTEM, NULL, true, NULL);
4784 typedef std::map <std::string, boost::variant <std::vector <std::string> > > PropDict;
4785 DBusClientCall1<PropDict> getProp(*this,"GetProperties");
4786 getProp (boost::bind(&ConnmanClient::getPropCb, this, _1, _2));
4787 m_propertyChanged.activate(boost::bind(&ConnmanClient::propertyChanged, this, _1, _2));
4789 SE_LOG_ERROR (NULL, NULL, "DBus connection setup for connman failed");
4793 void ConnmanClient::getPropCb (const std::map <std::string,
4794 boost::variant <std::vector <std::string> > >& props, const string &error){
4795 if (!error.empty()) {
4796 if (error == "org.freedesktop.DBus.Error.ServiceUnknown") {
4797 // ensure there is still first set of singal set in case of no
4798 // connman available
4799 m_server.getPresenceStatus().updatePresenceStatus (true, true);
4800 SE_LOG_DEBUG (NULL, NULL, "No connman service available %s", error.c_str());
4803 SE_LOG_DEBUG (NULL, NULL, "error in connmanCallback %s", error.c_str());
4807 typedef std::pair <std::string, boost::variant <std::vector <std::string> > > element;
4808 bool httpPresence = false, btPresence = false;
4809 BOOST_FOREACH (element entry, props) {
4810 //match connected for HTTP based peers (wifi/wimax/ethernet)
4811 if (entry.first == "ConnectedTechnologies") {
4812 std::vector <std::string> connected = boost::get <std::vector <std::string> > (entry.second);
4813 BOOST_FOREACH (std::string tech, connected) {
4814 if (boost::iequals (tech, "wifi") || boost::iequals (tech, "ethernet")
4815 || boost::iequals (tech, "wimax")) {
4816 httpPresence = true;
4820 } else if (entry.first == "AvailableTechnologies") {
4821 std::vector <std::string> enabled = boost::get <std::vector <std::string> > (entry.second);
4822 BOOST_FOREACH (std::string tech, enabled){
4823 if (boost::iequals (tech, "bluetooth")) {
4832 //now delivering the signals
4833 m_server.getPresenceStatus().updatePresenceStatus (httpPresence, btPresence);
4836 void ConnmanClient::propertyChanged(const string &name,
4837 const boost::variant<vector<string>, string> &prop)
4839 bool httpPresence=false, btPresence=false;
4840 bool httpChanged=false, btChanged=false;
4841 if (boost::iequals(name, "ConnectedTechnologies")) {
4843 vector<string> connected = boost::get<vector<string> >(prop);
4844 BOOST_FOREACH (std::string tech, connected) {
4845 if (boost::iequals (tech, "wifi") || boost::iequals (tech, "ethernet")
4846 || boost::iequals (tech, "wimax")) {
4851 } else if (boost::iequals (name, "AvailableTechnologies")){
4853 vector<string> enabled = boost::get<vector<string> >(prop);
4854 BOOST_FOREACH (std::string tech, enabled){
4855 if (boost::iequals (tech, "bluetooth")) {
4862 m_server.getPresenceStatus().updatePresenceStatus (httpPresence, PresenceStatus::HTTP_TRANSPORT);
4863 } else if (btChanged) {
4864 m_server.getPresenceStatus().updatePresenceStatus (btPresence, PresenceStatus::BT_TRANSPORT);
4869 /********************** DBusServer implementation ******************/
4871 void DBusServer::clientGone(Client *c)
4873 for(Clients_t::iterator it = m_clients.begin();
4874 it != m_clients.end();
4876 if (it->second.get() == c) {
4877 SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s has disconnected",
4879 autoTermUnref(it->second->getAttachCount());
4880 m_clients.erase(it);
4884 SE_LOG_DEBUG(NULL, NULL, "unknown client has disconnected?!");
4887 std::string DBusServer::getNextSession()
4889 // Make the session ID somewhat random. This protects to
4890 // some extend against injecting unwanted messages into the
4893 if (!m_lastSession) {
4896 return StringPrintf("%u%u", rand(), m_lastSession);
4899 vector<string> DBusServer::getCapabilities()
4901 // Note that this is tested by test-dbus.py in
4902 // TestDBusServer.testCapabilities, update the test when adding
4904 vector<string> capabilities;
4906 capabilities.push_back("ConfigChanged");
4907 capabilities.push_back("GetConfigName");
4908 capabilities.push_back("Notifications");
4909 capabilities.push_back("Version");
4910 capabilities.push_back("SessionFlags");
4911 capabilities.push_back("SessionAttach");
4912 return capabilities;
4915 StringMap DBusServer::getVersions()
4919 versions["version"] = VERSION;
4920 versions["system"] = EDSAbiWrapperInfo();
4921 versions["backends"] = SyncSource::backendsInfo();
4925 void DBusServer::attachClient(const Caller_t &caller,
4926 const boost::shared_ptr<Watch> &watch)
4928 boost::shared_ptr<Client> client = addClient(getConnection(),
4932 client->increaseAttachCount();
4935 void DBusServer::detachClient(const Caller_t &caller)
4937 boost::shared_ptr<Client> client = findClient(caller);
4940 client->decreaseAttachCount();
4944 void DBusServer::setNotifications(bool enabled,
4945 const Caller_t &caller,
4946 const string & /* notifications */)
4948 boost::shared_ptr<Client> client = findClient(caller);
4949 if (client && client->getAttachCount()) {
4950 client->setNotificationsEnabled(enabled);
4952 SE_THROW("client not attached, not allowed to change notifications");
4956 bool DBusServer::notificationsEnabled()
4958 for(Clients_t::iterator it = m_clients.begin();
4959 it != m_clients.end();
4961 if (!it->second->getNotificationsEnabled()) {
4968 void DBusServer::connect(const Caller_t &caller,
4969 const boost::shared_ptr<Watch> &watch,
4970 const StringMap &peer,
4971 bool must_authenticate,
4972 const std::string &session,
4973 DBusObject_t &object)
4975 if (!session.empty()) {
4976 // reconnecting to old connection is not implemented yet
4977 throw std::runtime_error("not implemented");
4979 std::string new_session = getNextSession();
4981 boost::shared_ptr<Connection> c(new Connection(*this,
4985 must_authenticate));
4986 SE_LOG_DEBUG(NULL, NULL, "connecting D-Bus client %s with connection %s '%s'",
4989 c->m_description.c_str());
4991 boost::shared_ptr<Client> client = addClient(getConnection(),
4997 object = c->getPath();
5000 void DBusServer::startSessionWithFlags(const Caller_t &caller,
5001 const boost::shared_ptr<Watch> &watch,
5002 const std::string &server,
5003 const std::vector<std::string> &flags,
5004 DBusObject_t &object)
5006 boost::shared_ptr<Client> client = addClient(getConnection(),
5009 std::string new_session = getNextSession();
5010 boost::shared_ptr<Session> session = Session::createSession(*this,
5011 "is this a client or server session?",
5015 client->attach(session);
5016 session->activate();
5018 object = session->getPath();
5021 void DBusServer::checkPresence(const std::string &server,
5022 std::string &status,
5023 std::vector<std::string> &transports)
5025 return m_presence.checkPresence(server, status, transports);
5028 void DBusServer::getSessions(std::vector<DBusObject_t> &sessions)
5030 sessions.reserve(m_workQueue.size() + 1);
5031 if (m_activeSession) {
5032 sessions.push_back(m_activeSession->getPath());
5034 BOOST_FOREACH(boost::weak_ptr<Session> &session, m_workQueue) {
5035 boost::shared_ptr<Session> s = session.lock();
5037 sessions.push_back(s->getPath());
5042 DBusServer::DBusServer(GMainLoop *loop, const DBusConnectionPtr &conn, int duration) :
5043 DBusObjectHelper(conn.get(),
5044 "/org/syncevolution/Server",
5045 "org.syncevolution.Server",
5046 boost::bind(&DBusServer::autoTermCallback, this)),
5048 m_lastSession(time(NULL)),
5049 m_activeSession(NULL),
5051 m_bluezManager(*this),
5052 sessionChanged(*this, "SessionChanged"),
5053 presence(*this, "Presence"),
5054 templatesChanged(*this, "TemplatesChanged"),
5055 configChanged(*this, "ConfigChanged"),
5056 infoRequest(*this, "InfoRequest"),
5057 logOutput(*this, "LogOutput"),
5061 m_autoTerm(m_autoSync.preventTerm() ? -1 : duration), //if there is any task in auto sync, prevent auto termination
5062 m_parentLogger(LoggerBase::instance())
5065 gettimeofday(&tv, NULL);
5067 add(this, &DBusServer::getCapabilities, "GetCapabilities");
5068 add(this, &DBusServer::getVersions, "GetVersions");
5069 add(this, &DBusServer::attachClient, "Attach");
5070 add(this, &DBusServer::detachClient, "Detach");
5071 add(this, &DBusServer::enableNotifications, "EnableNotifications");
5072 add(this, &DBusServer::disableNotifications, "DisableNotifications");
5073 add(this, &DBusServer::connect, "Connect");
5074 add(this, &DBusServer::startSession, "StartSession");
5075 add(this, &DBusServer::startSessionWithFlags, "StartSessionWithFlags");
5076 add(this, &DBusServer::getConfigs, "GetConfigs");
5077 add(this, &DBusServer::getConfig, "GetConfig");
5078 add(this, &DBusServer::getReports, "GetReports");
5079 add(this, &DBusServer::checkSource, "CheckSource");
5080 add(this, &DBusServer::getDatabases, "GetDatabases");
5081 add(this, &DBusServer::checkPresence, "CheckPresence");
5082 add(this, &DBusServer::getSessions, "GetSessions");
5083 add(this, &DBusServer::infoResponse, "InfoResponse");
5084 add(sessionChanged);
5085 add(templatesChanged);
5091 LoggerBase::pushLogger(this);
5092 setLevel(LoggerBase::DEBUG);
5095 DBusServer::~DBusServer()
5097 // make sure all other objects are gone before destructing ourselves
5098 m_syncSession.reset();
5099 m_workQueue.clear();
5101 LoggerBase::popLogger();
5104 void DBusServer::run()
5106 while (!shutdownRequested) {
5107 if (!m_activeSession ||
5108 !m_activeSession->readyToRun()) {
5109 g_main_loop_run(m_loop);
5111 if (m_activeSession &&
5112 m_activeSession->readyToRun()) {
5113 // this session must be owned by someone, otherwise
5114 // it would not be set as active session
5115 boost::shared_ptr<Session> session = m_activeSessionRef.lock();
5117 throw runtime_error("internal error: session no longer available");
5120 // ensure that the session doesn't go away
5121 m_syncSession.swap(session);
5122 m_activeSession->run();
5123 } catch (const std::exception &ex) {
5124 SE_LOG_ERROR(NULL, NULL, "%s", ex.what());
5126 SE_LOG_ERROR(NULL, NULL, "unknown error");
5128 session.swap(m_syncSession);
5129 dequeue(session.get());
5132 if (m_autoSync.hasTask()) {
5133 // if there is at least one pending task and no session is created for auto sync,
5134 // pick one task and create a session
5135 m_autoSync.startTask();
5137 // Make sure check whether m_activeSession is owned by autosync
5138 // Otherwise activeSession is owned by AutoSyncManager but it never
5139 // be ready to run. Because methods of Session, like 'sync', are able to be
5140 // called when it is active.
5141 if (m_autoSync.hasActiveSession())
5143 // if the autosync is the active session, then invoke 'sync'
5144 // to make it ready to run
5145 m_autoSync.prepare();
5152 * look up client by its ID
5154 boost::shared_ptr<Client> DBusServer::findClient(const Caller_t &ID)
5156 for(Clients_t::iterator it = m_clients.begin();
5157 it != m_clients.end();
5159 if (it->second->m_ID == ID) {
5163 return boost::shared_ptr<Client>();
5166 boost::shared_ptr<Client> DBusServer::addClient(const DBusConnectionPtr &conn,
5168 const boost::shared_ptr<Watch> &watch)
5170 boost::shared_ptr<Client> client(findClient(ID));
5174 client.reset(new Client(*this, ID));
5175 // add to our list *before* checking that peer exists, so
5176 // that clientGone() can remove it if the check fails
5177 m_clients.push_back(std::make_pair(watch, client));
5178 watch->setCallback(boost::bind(&DBusServer::clientGone, this, client.get()));
5183 void DBusServer::detach(Resource *resource)
5185 BOOST_FOREACH(const Clients_t::value_type &client_entry,
5187 client_entry.second->detachAll(resource);
5191 void DBusServer::enqueue(const boost::shared_ptr<Session> &session)
5193 WorkQueue_t::iterator it = m_workQueue.end();
5194 while (it != m_workQueue.begin()) {
5196 if (it->lock()->getPriority() <= session->getPriority()) {
5201 m_workQueue.insert(it, session);
5206 int DBusServer::killSessions(const std::string &peerDeviceID)
5210 WorkQueue_t::iterator it = m_workQueue.begin();
5211 while (it != m_workQueue.end()) {
5212 boost::shared_ptr<Session> session = it->lock();
5213 if (session && session->getPeerDeviceID() == peerDeviceID) {
5214 SE_LOG_DEBUG(NULL, NULL, "removing pending session %s because it matches deviceID %s",
5215 session->getSessionID().c_str(),
5216 peerDeviceID.c_str());
5217 // remove session and its corresponding connection
5218 boost::shared_ptr<Connection> c = session->getConnection().lock();
5222 it = m_workQueue.erase(it);
5229 if (m_activeSession &&
5230 m_activeSession->getPeerDeviceID() == peerDeviceID) {
5231 SE_LOG_DEBUG(NULL, NULL, "aborting active session %s because it matches deviceID %s",
5232 m_activeSession->getSessionID().c_str(),
5233 peerDeviceID.c_str());
5235 // abort, even if not necessary right now
5236 m_activeSession->abort();
5238 // TODO: catch only that exception which indicates
5239 // incorrect use of the function
5241 dequeue(m_activeSession);
5248 void DBusServer::dequeue(Session *session)
5250 if (m_syncSession.get() == session) {
5251 // This is the running sync session.
5252 // It's not in the work queue and we have to
5253 // keep it active, so nothing to do.
5257 for (WorkQueue_t::iterator it = m_workQueue.begin();
5258 it != m_workQueue.end();
5260 if (it->lock().get() == session) {
5261 // remove from queue
5262 m_workQueue.erase(it);
5263 // session was idle, so nothing else to do
5268 if (m_activeSession == session) {
5269 // The session is releasing the lock, so someone else might
5271 session->setActive(false);
5272 sessionChanged(session->getPath(), false);
5273 m_activeSession = NULL;
5274 m_activeSessionRef.reset();
5280 void DBusServer::checkQueue()
5282 if (m_activeSession) {
5287 while (!m_workQueue.empty()) {
5288 boost::shared_ptr<Session> session = m_workQueue.front().lock();
5289 m_workQueue.pop_front();
5291 // activate the session
5292 m_activeSession = session.get();
5293 m_activeSessionRef = session;
5294 session->setActive(true);
5295 sessionChanged(session->getPath(), true);
5296 //if the active session is changed, give a chance to quit the main loop
5297 //and make it ready to run if it is owned by AutoSyncManager.
5298 //Otherwise, server might be blocked.
5299 g_main_loop_quit(m_loop);
5305 bool DBusServer::callTimeout(const boost::shared_ptr<Timeout> &timeout, const boost::function<bool ()> &callback)
5308 m_timeouts.remove(timeout);
5315 void DBusServer::addTimeout(const boost::function<bool ()> &callback,
5318 boost::shared_ptr<Timeout> timeout(new Timeout);
5319 m_timeouts.push_back(timeout);
5320 timeout->activate(seconds,
5321 boost::bind(&DBusServer::callTimeout,
5323 // avoid copying the shared pointer here,
5324 // otherwise the Timeout will never be deleted
5325 boost::ref(m_timeouts.back()),
5329 void DBusServer::infoResponse(const Caller_t &caller,
5330 const std::string &id,
5331 const std::string &state,
5332 const std::map<string, string> &response)
5334 InfoReqMap::iterator it = m_infoReqMap.find(id);
5335 // if not found, ignore
5336 if(it != m_infoReqMap.end()) {
5337 boost::shared_ptr<InfoReq> infoReq = it->second.lock();
5338 infoReq->setResponse(caller, state, response);
5342 boost::shared_ptr<InfoReq> DBusServer::createInfoReq(const string &type,
5343 const std::map<string, string> ¶meters,
5344 const Session *session)
5346 boost::shared_ptr<InfoReq> infoReq(new InfoReq(*this, type, parameters, session));
5347 boost::weak_ptr<InfoReq> item(infoReq) ;
5348 m_infoReqMap.insert(pair<string, boost::weak_ptr<InfoReq> >(infoReq->getId(), item));
5352 std::string DBusServer::getNextInfoReq()
5354 return StringPrintf("%u", ++m_lastInfoReq);
5357 void DBusServer::emitInfoReq(const InfoReq &req)
5359 infoRequest(req.getId(),
5360 req.getSessionPath(),
5361 req.getInfoStateStr(),
5367 void DBusServer::removeInfoReq(const InfoReq &req)
5369 // remove InfoRequest from hash map
5370 InfoReqMap::iterator it = m_infoReqMap.find(req.getId());
5371 if(it != m_infoReqMap.end()) {
5372 m_infoReqMap.erase(it);
5376 void DBusServer::getDeviceList(SyncConfig::DeviceList &devices)
5378 //wait bluez or other device managers
5379 while(!m_bluezManager.isDone()) {
5380 g_main_loop_run(m_loop);
5384 devices = m_syncDevices;
5387 void DBusServer::addPeerTempl(const string &templName,
5388 const boost::shared_ptr<SyncConfig::TemplateDescription> peerTempl)
5390 std::string lower = templName;
5391 boost::to_lower(lower);
5392 m_matchedTempls.insert(MatchedTemplates::value_type(lower, peerTempl));
5395 boost::shared_ptr<SyncConfig::TemplateDescription> DBusServer::getPeerTempl(const string &peer)
5397 std::string lower = peer;
5398 boost::to_lower(lower);
5399 MatchedTemplates::iterator it = m_matchedTempls.find(lower);
5400 if(it != m_matchedTempls.end()) {
5403 return boost::shared_ptr<SyncConfig::TemplateDescription>();
5407 bool DBusServer::getDevice(const string &deviceId, SyncConfig::DeviceDescription &device)
5409 SyncConfig::DeviceList::iterator syncDevIt;
5410 for(syncDevIt = m_syncDevices.begin(); syncDevIt != m_syncDevices.end(); ++syncDevIt) {
5411 if(boost::equals(syncDevIt->m_deviceId, deviceId)) {
5412 device = *syncDevIt;
5419 void DBusServer::addDevice(const SyncConfig::DeviceDescription &device)
5421 SyncConfig::DeviceList::iterator it;
5422 for(it = m_syncDevices.begin(); it != m_syncDevices.end(); ++it) {
5423 if(boost::iequals(it->m_deviceId, device.m_deviceId)) {
5427 if(it == m_syncDevices.end()) {
5428 m_syncDevices.push_back(device);
5433 void DBusServer::removeDevice(const string &deviceId)
5435 SyncConfig::DeviceList::iterator syncDevIt;
5436 for(syncDevIt = m_syncDevices.begin(); syncDevIt != m_syncDevices.end(); ++syncDevIt) {
5437 if(boost::equals(syncDevIt->m_deviceId, deviceId)) {
5438 m_syncDevices.erase(syncDevIt);
5445 void DBusServer::updateDevice(const string &deviceId,
5446 const SyncConfig::DeviceDescription &device)
5448 SyncConfig::DeviceList::iterator it;
5449 for(it = m_syncDevices.begin(); it != m_syncDevices.end(); ++it) {
5450 if(boost::iequals(it->m_deviceId, deviceId)) {
5458 void DBusServer::messagev(Level level,
5462 const char *function,
5466 // iterating over args in messagev() is destructive, must make a copy first
5468 va_copy(argsCopy, args);
5469 m_parentLogger.messagev(level, prefix, file, line, function, format, args);
5470 string log = StringPrintfV(format, argsCopy);
5473 // prefix is used to set session path
5474 // for general server output, the object path field is dbus server
5475 // the object path can't be empty for object paths prevent using empty string.
5476 string strLevel = Logger::levelToStr(level);
5477 if(m_activeSession) {
5478 logOutput(m_activeSession->getPath(), strLevel, log);
5480 logOutput(getPath(), strLevel, log);
5484 /********************** InfoReq implementation ******************/
5485 InfoReq::InfoReq(DBusServer &server,
5487 const InfoMap ¶meters,
5488 const Session *session,
5490 m_server(server), m_session(session), m_infoState(IN_REQ),
5491 m_status(ST_RUN), m_type(type), m_param(parameters),
5492 m_timeout(timeout), m_timer(m_timeout * 1000)
5494 m_id = m_server.getNextInfoReq();
5495 m_server.emitInfoReq(*this);
5503 m_server.removeInfoReq(*this);
5506 InfoReq::Status InfoReq::check()
5508 if(m_status == ST_RUN) {
5509 // give an opportunity to poll the sources on the main context
5510 g_main_context_iteration(g_main_loop_get_context(m_server.getLoop()), false);
5516 bool InfoReq::getResponse(InfoMap &response)
5518 if (m_status == ST_OK) {
5519 response = m_response;
5525 InfoReq::Status InfoReq::wait(InfoMap &response, uint32_t interval)
5527 // give a chance to check whether it has been timeout
5529 if(m_status == ST_RUN) {
5530 guint checkSource = g_timeout_add_seconds(interval,
5531 (GSourceFunc) checkCallback,
5532 static_cast<gpointer>(this));
5533 while(m_status == ST_RUN) {
5534 g_main_context_iteration(g_main_loop_get_context(m_server.getLoop()), true);
5537 // if the source is not removed
5538 if(m_status != ST_TIMEOUT && m_status != ST_CANCEL) {
5539 g_source_remove(checkSource);
5542 if (m_status == ST_OK) {
5543 response = m_response;
5548 void InfoReq::cancel()
5550 if(m_status == ST_RUN) {
5553 m_status = ST_CANCEL;
5557 string InfoReq::statusToString(Status status)
5573 string InfoReq::infoStateToString(InfoState state)
5587 gboolean InfoReq::checkCallback(gpointer data)
5589 // TODO: check abort and suspend(MB#8730)
5591 // if InfoRequest("request") is sent and waiting for InfoResponse("working"),
5592 // add a timeout mechanism
5593 InfoReq *req = static_cast<InfoReq*>(data);
5594 if (req->checkTimeout()) {
5600 bool InfoReq::checkTimeout()
5602 // if waiting for client response, check time out
5603 if(m_status == ST_RUN) {
5604 if (m_timer.timeout()) {
5605 m_status = ST_TIMEOUT;
5612 void InfoReq::setResponse(const Caller_t &caller, const string &state, const InfoMap &response)
5614 if(m_status != ST_RUN) {
5616 } else if(m_infoState == IN_REQ && state == "working") {
5618 m_infoState = IN_WAIT;
5619 m_server.emitInfoReq(*this);
5620 //reset the timer, used to check timeout
5622 } else if(m_infoState == IN_WAIT && state == "response") {
5623 m_response = response;
5630 void InfoReq::done()
5632 if (m_infoState != IN_DONE) {
5633 m_infoState = IN_DONE;
5634 m_server.emitInfoReq(*this);
5638 /********************** BluezManager implementation ******************/
5639 BluezManager::BluezManager(DBusServer &server) :
5641 m_adapterChanged(*this, "DefaultAdapterChanged")
5643 m_bluezConn = b_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, true, NULL);
5646 DBusClientCall1<DBusObject_t> getAdapter(*this, "DefaultAdapter");
5647 getAdapter(boost::bind(&BluezManager::defaultAdapterCb, this, _1, _2 ));
5648 m_adapterChanged.activate(boost::bind(&BluezManager::defaultAdapterChanged, this, _1));
5654 void BluezManager::defaultAdapterChanged(const DBusObject_t &adapter)
5657 //remove devices that belong to this original adapter
5659 BOOST_FOREACH(boost::shared_ptr<BluezDevice> &device, m_adapter->getDevices()) {
5660 m_server.removeDevice(device->getMac());
5664 defaultAdapterCb(adapter, error);
5667 void BluezManager::defaultAdapterCb(const DBusObject_t &adapter, const string &error)
5669 if(!error.empty()) {
5670 SE_LOG_DEBUG (NULL, NULL, "Error in calling DefaultAdapter of Interface org.bluez.Manager: %s", error.c_str());
5674 m_adapter.reset(new BluezAdapter(*this, adapter));
5677 BluezManager::BluezAdapter::BluezAdapter(BluezManager &manager, const string &path)
5678 : m_manager(manager), m_path(path), m_devNo(0), m_devReplies(0),
5679 m_deviceRemoved(*this, "DeviceRemoved"), m_deviceAdded(*this, "DeviceCreated")
5681 DBusClientCall1<std::vector<DBusObject_t> > listDevices(*this, "ListDevices");
5682 listDevices(boost::bind(&BluezAdapter::listDevicesCb, this, _1, _2));
5683 m_deviceRemoved.activate(boost::bind(&BluezAdapter::deviceRemoved, this, _1));
5684 m_deviceAdded.activate(boost::bind(&BluezAdapter::deviceCreated, this, _1));
5687 void BluezManager::BluezAdapter::listDevicesCb(const std::vector<DBusObject_t> &devices, const string &error)
5689 if(!error.empty()) {
5690 SE_LOG_DEBUG (NULL, NULL, "Error in calling ListDevices of Interface org.bluez.Adapter: %s", error.c_str());
5694 m_devNo = devices.size();
5695 BOOST_FOREACH(const DBusObject_t &device, devices) {
5696 boost::shared_ptr<BluezDevice> bluezDevice(new BluezDevice(*this, device));
5697 m_devices.push_back(bluezDevice);
5702 void BluezManager::BluezAdapter::deviceRemoved(const DBusObject_t &object)
5705 std::vector<boost::shared_ptr<BluezDevice> >::iterator devIt;
5706 for(devIt = m_devices.begin(); devIt != m_devices.end(); ++devIt) {
5707 if(boost::equals((*devIt)->getPath(), object)) {
5708 address = (*devIt)->m_mac;
5709 if((*devIt)->m_reply) {
5713 m_devices.erase(devIt);
5717 m_manager.m_server.removeDevice(address);
5720 void BluezManager::BluezAdapter::deviceCreated(const DBusObject_t &object)
5723 boost::shared_ptr<BluezDevice> bluezDevice(new BluezDevice(*this, object));
5724 m_devices.push_back(bluezDevice);
5727 BluezManager::BluezDevice::BluezDevice (BluezAdapter &adapter, const string &path)
5728 : m_adapter(adapter), m_path(path), m_reply(false), m_propertyChanged(*this, "PropertyChanged")
5730 DBusClientCall1<PropDict> getProperties(*this, "GetProperties");
5731 getProperties(boost::bind(&BluezDevice::getPropertiesCb, this, _1, _2));
5733 m_propertyChanged.activate(boost::bind(&BluezDevice::propertyChanged, this, _1, _2));
5736 void BluezManager::BluezDevice::checkSyncService(const std::vector<std::string> &uuids)
5738 static const char * SYNCML_CLIENT_UUID = "00000002-0000-1000-8000-0002ee000002";
5739 bool hasSyncService = false;
5740 DBusServer &server = m_adapter.m_manager.m_server;
5741 BOOST_FOREACH(const string &uuid, uuids) {
5742 //if the device has sync service, add it to the device list
5743 if(boost::iequals(uuid, SYNCML_CLIENT_UUID)) {
5744 hasSyncService = true;
5745 if(!m_mac.empty()) {
5746 server.addDevice(SyncConfig::DeviceDescription(m_mac, m_name, SyncConfig::MATCH_FOR_SERVER_MODE));
5751 // if sync service is not available now, possible to remove device
5752 if(!hasSyncService && !m_mac.empty()) {
5753 server.removeDevice(m_mac);
5757 void BluezManager::BluezDevice::getPropertiesCb(const PropDict &props, const string &error)
5759 m_adapter.m_devReplies++;
5761 if(!error.empty()) {
5762 SE_LOG_DEBUG (NULL, NULL, "Error in calling GetProperties of Interface org.bluez.Device: %s", error.c_str());
5764 PropDict::const_iterator it = props.find("Name");
5765 if(it != props.end()) {
5766 m_name = boost::get<string>(it->second);
5768 it = props.find("Address");
5769 if(it != props.end()) {
5770 m_mac = boost::get<string>(it->second);
5773 PropDict::const_iterator uuids = props.find("UUIDs");
5774 if(uuids != props.end()) {
5775 const std::vector<std::string> uuidVec = boost::get<std::vector<std::string> >(uuids->second);
5776 checkSyncService(uuidVec);
5779 m_adapter.checkDone();
5782 void BluezManager::BluezDevice::propertyChanged(const string &name,
5783 const boost::variant<vector<string>, string> &prop)
5785 DBusServer &server = m_adapter.m_manager.m_server;
5786 if(boost::iequals(name, "Name")) {
5787 m_name = boost::get<std::string>(prop);
5788 SyncConfig::DeviceDescription device;
5789 if(server.getDevice(m_mac, device)) {
5790 device.m_fingerprint = m_name;
5791 server.updateDevice(m_mac, device);
5793 } else if(boost::iequals(name, "UUIDs")) {
5794 const std::vector<std::string> uuidVec = boost::get<std::vector<std::string> >(prop);
5795 checkSyncService(uuidVec);
5796 } else if(boost::iequals(name, "Address")) {
5797 string mac = boost::get<std::string>(prop);
5798 SyncConfig::DeviceDescription device;
5799 if(server.getDevice(m_mac, device)) {
5800 device.m_deviceId = mac;
5801 server.updateDevice(m_mac, device);
5807 /************************ AutoSyncManager ******************/
5808 void AutoSyncManager::init()
5811 SyncConfig::ConfigList list = SyncConfig::getConfigs();
5812 BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server, list) {
5813 initConfig(server.first);
5817 void AutoSyncManager::initConfig(const string &configName)
5819 SyncConfig config (configName);
5820 if(!config.exists()) {
5823 vector<string> urls = config.getSyncURL();
5824 string autoSync = config.getAutoSync();
5826 //enable http and bt?
5827 bool http = false, bt = false;
5828 if(autoSync.empty() || boost::iequals(autoSync, "0")
5829 || boost::iequals(autoSync, "f")) {
5832 } else if(boost::iequals(autoSync, "1") || boost::iequals(autoSync, "t")) {
5836 vector<string> options;
5837 boost::split(options, autoSync, boost::is_any_of(","));
5838 BOOST_FOREACH(string op, options) {
5839 if(boost::iequals(op, "http")) {
5841 } else if(boost::iequals(op, "obex-bt")) {
5847 unsigned int interval = config.getAutoSyncInterval();
5848 unsigned int duration = config.getAutoSyncDelay();
5850 BOOST_FOREACH(string url, urls) {
5851 if((boost::istarts_with(url, "http") && http)
5852 || (boost::istarts_with(url, "obex-bt") && bt)) {
5853 AutoSyncTask syncTask(configName, duration, url);
5854 PeerMap::iterator it = m_peerMap.find(interval);
5855 if(it != m_peerMap.end()) {
5856 it->second->push_back(syncTask);
5858 boost::shared_ptr<AutoSyncTaskList> list(new AutoSyncTaskList(*this, interval));
5859 list->push_back(syncTask);
5860 list->createTimeoutSource();
5861 m_peerMap.insert(std::make_pair(interval, list));
5867 void AutoSyncManager::remove(const string &configName)
5869 //wipe out tasks in the m_peerMap
5870 PeerMap::iterator it = m_peerMap.begin();
5871 while(it != m_peerMap.end()) {
5872 boost::shared_ptr<AutoSyncTaskList> &list = it->second;
5873 AutoSyncTaskList::iterator taskIt = list->begin();
5874 while(taskIt != list->end()) {
5875 if(boost::iequals(taskIt->m_peer, configName)) {
5876 taskIt = list->erase(taskIt);
5881 //if list is empty, remove the list from map
5883 PeerMap::iterator erased = it++;
5884 m_peerMap.erase(erased);
5890 //wipe out scheduled tasks in the working queue based on configName
5891 list<AutoSyncTask>::iterator qit = m_workQueue.begin();
5892 while(qit != m_workQueue.end()) {
5893 if(boost::iequals(qit->m_peer, configName)) {
5894 qit = m_workQueue.erase(qit);
5901 void AutoSyncManager::update(const string &configName)
5903 // remove task from m_peerMap and tasks in the working queue for this config
5905 // re-load the config and re-init peer map
5906 initConfig(configName);
5908 //don't clear if the task is running
5909 if(m_session && !hasActiveSession()
5910 && boost::iequals(m_session->getConfigName(), configName)) {
5911 m_server.dequeue(m_session.get());
5913 m_activeTask.reset();
5918 void AutoSyncManager::scheduleAll()
5920 BOOST_FOREACH(PeerMap::value_type &elem, m_peerMap) {
5921 elem.second->scheduleTaskList();
5925 bool AutoSyncManager::addTask(const AutoSyncTask &syncTask)
5927 if(taskLikelyToRun(syncTask)) {
5928 m_workQueue.push_back(syncTask);
5934 bool AutoSyncManager::findTask(const AutoSyncTask &syncTask)
5936 if(m_activeTask && *m_activeTask == syncTask) {
5939 BOOST_FOREACH(const AutoSyncTask &task, m_workQueue) {
5940 if(task == syncTask) {
5947 bool AutoSyncManager::taskLikelyToRun(const AutoSyncTask &syncTask)
5949 PresenceStatus &status = m_server.getPresenceStatus();
5950 // avoid doing any checking of task list if http and bt presence are false
5951 if(!status.getHttpPresence() && !status.getBtPresence()) {
5955 if(boost::istarts_with(syncTask.m_url, "http") && status.getHttpPresence()) {
5956 // don't add duplicate tasks
5957 if(!findTask(syncTask)) {
5958 Timer& timer = status.getHttpTimer();
5959 // if the time peer have been around is longer than 'autoSyncDelay',
5961 if (timer.timeout(syncTask.m_delay * 1000 /* seconds to milliseconds */)) {
5965 } else if (boost::istarts_with(syncTask.m_url, "obex-bt") && status.getBtPresence()) {
5966 // don't add duplicate tasks
5967 if(!findTask(syncTask)) {
5974 void AutoSyncManager::startTask()
5976 // get the front task and run a sync
5977 // if there has been a session for the front task, do nothing
5978 if(hasTask() && !m_session) {
5979 m_activeTask.reset(new AutoSyncTask(m_workQueue.front()));
5980 m_workQueue.pop_front();
5981 string newSession = m_server.getNextSession();
5982 m_session = Session::createSession(m_server,
5984 m_activeTask->m_peer,
5986 m_session->setPriority(Session::PRI_AUTOSYNC);
5987 m_session->addListener(this);
5988 m_server.enqueue(m_session);
5992 bool AutoSyncManager::hasActiveSession()
5994 return m_session && m_session->getActive();
5997 void AutoSyncManager::prepare()
5999 if(m_session && m_session->getActive()) {
6000 // now a config may contain many urls, so replace it with our own temporarily
6001 // otherwise it only picks the first one
6002 ReadOperations::Config_t config;
6003 StringMap stringMap;
6004 stringMap["syncURL"] = m_activeTask->m_url;
6005 config[""] = stringMap;
6006 m_session->setConfig(true, true, config);
6009 Session::SourceModes_t sourceModes;
6010 m_session->sync(mode, sourceModes);
6014 void AutoSyncManager::syncSuccessStart()
6016 m_syncSuccessStart = true;
6017 SE_LOG_INFO(NULL, NULL,"Automatic sync for '%s' has been successfully started.\n", m_activeTask->m_peer.c_str());
6019 if (m_server.notificationsEnabled()) {
6020 string summary = StringPrintf(_("%s is syncing"), m_activeTask->m_peer.c_str());
6021 string body = StringPrintf(_("We have just started to sync your computer with the %s sync service."), m_activeTask->m_peer.c_str());
6022 //TODO: set config information for 'sync-ui'
6023 m_notify.send(summary.c_str(), body.c_str());
6028 void AutoSyncManager::syncDone(SyncMLStatus status)
6030 SE_LOG_INFO(NULL, NULL,"Automatic sync for '%s' has been done.\n", m_activeTask->m_peer.c_str());
6032 if (m_server.notificationsEnabled()) {
6033 // send a notification to notification server
6034 string summary, body;
6035 if(m_syncSuccessStart && status == STATUS_OK) {
6036 // if sync is successfully started and done
6037 summary = StringPrintf(_("%s sync complete"), m_activeTask->m_peer.c_str());
6038 body = StringPrintf(_("We have just finished syncing your computer with the %s sync service."), m_activeTask->m_peer.c_str());
6039 //TODO: set config information for 'sync-ui'
6040 m_notify.send(summary.c_str(), body.c_str());
6041 } else if(m_syncSuccessStart || (!m_syncSuccessStart && status == STATUS_FATAL)) {
6042 //if sync is successfully started and has errors, or not started successful with a fatal problem
6043 summary = StringPrintf(_("Sync problem."));
6044 body = StringPrintf(_("Sorry, there's a problem with your sync that you need to attend to."));
6045 //TODO: set config information for 'sync-ui'
6046 m_notify.send(summary.c_str(), body.c_str());
6051 m_activeTask.reset();
6052 m_syncSuccessStart = false;
6056 AutoSyncManager::Notification::Notification()
6058 bindtextdomain (GETTEXT_PACKAGE, SYNCEVOLUTION_LOCALEDIR);
6059 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
6060 textdomain (GETTEXT_PACKAGE);
6061 m_init = notify_init("SyncEvolution");
6063 m_notification = NULL;
6064 // check whether 'actions' are supported by notification server
6066 GList *list = notify_get_server_caps();
6068 for(; list != NULL; list = list->next) {
6069 if(boost::iequals((char *)list->data, "actions")) {
6077 AutoSyncManager::Notification::~Notification()
6084 void AutoSyncManager::Notification::notifyAction(NotifyNotification *notify,
6088 if(boost::iequals("view", action)) {
6090 if((pid = fork()) == 0) {
6091 //search sync-ui from $PATH
6092 if(execlp("sync-ui", "sync-ui", (const char*)0) < 0) {
6097 //if dismiss, ignore
6100 void AutoSyncManager::Notification::send(const char *summary,
6102 const char *viewParams)
6107 if(m_notification) {
6108 notify_notification_clear_actions(m_notification);
6109 notify_notification_close(m_notification, NULL);
6111 m_notification = notify_notification_new(summary, body, NULL, NULL);
6112 //if actions are not supported, don't add actions
6113 //An example is Ubuntu Notify OSD. It uses an alert box
6114 //instead of a bubble when a notification is appended with actions.
6115 //the alert box won't be closed until user inputs.
6116 //so disable it in case of no support of actions
6118 notify_notification_add_action(m_notification, "view", _("View"), notifyAction, (gpointer)viewParams, NULL);
6119 // Use "default" as ID because that is what mutter-moblin
6120 // recognizes: it then skips the action instead of adding it
6121 // in addition to its own "Dismiss" button (always added).
6122 notify_notification_add_action(m_notification, "default", _("Dismiss"), notifyAction, (gpointer)viewParams, NULL);
6124 notify_notification_show(m_notification, NULL);
6128 void AutoSyncManager::AutoSyncTaskList::createTimeoutSource()
6130 //if interval is 0, only run auto sync when changes are detected.
6132 m_source = g_timeout_add_seconds(m_interval, taskListTimeoutCb, static_cast<gpointer>(this));
6136 gboolean AutoSyncManager::AutoSyncTaskList::taskListTimeoutCb(gpointer data)
6138 AutoSyncTaskList *list = static_cast<AutoSyncTaskList*>(data);
6139 list->scheduleTaskList();
6143 void AutoSyncManager::AutoSyncTaskList::scheduleTaskList()
6145 BOOST_FOREACH(AutoSyncTask &syncTask, *this) {
6146 m_manager.addTask(syncTask);
6148 g_main_loop_quit(m_manager.m_server.getLoop());
6151 /**************************** main *************************/
6155 shutdownRequested = true;
6156 SyncContext::handleSignal(sig);
6157 g_main_loop_quit (loop);
6160 static bool parseDuration(int &duration, const char* value)
6164 } else if (boost::iequals(value, "unlimited")) {
6167 } else if ((duration = atoi(value)) > 0) {
6174 int main(int argc, char **argv)
6179 if(argv[opt][0] != '-') {
6182 if (boost::iequals(argv[opt], "--duration") ||
6183 boost::iequals(argv[opt], "-d")) {
6185 if(!parseDuration(duration, opt== argc ? NULL : argv[opt])) {
6186 std::cout << argv[opt-1] << ": unknown parameter value or not set" << std::endl;
6190 std::cout << argv[opt] << ": unknown parameter" << std::endl;
6197 g_thread_init(NULL);
6198 g_set_application_name("SyncEvolution");
6200 // Initializing a potential use of EDS early is necessary for
6201 // libsynthesis when compiled with
6202 // --enable-evolution-compatibility: in that mode libical will
6203 // only be found by libsynthesis after EDSAbiWrapperInit()
6204 // pulls it into the process by loading libecal.
6205 EDSAbiWrapperInit();
6207 loop = g_main_loop_new (NULL, FALSE);
6209 setvbuf(stderr, NULL, _IONBF, 0);
6210 setvbuf(stdout, NULL, _IONBF, 0);
6212 signal(SIGTERM, niam);
6213 signal(SIGINT, niam);
6215 LogRedirect redirect(true);
6216 redirectPtr = &redirect;
6218 // make daemon less chatty - long term this should be a command line option
6219 LoggerBase::instance().setLevel(LoggerBase::INFO);
6222 DBusConnectionPtr conn = b_dbus_setup_bus(DBUS_BUS_SESSION,
6223 "org.syncevolution",
6227 err.throwFailure("b_dbus_setup_bus()", " failed - server already running?");
6230 DBusServer server(loop, conn, duration);
6233 SE_LOG_INFO(NULL, NULL, "%s: ready to run", argv[0]);
6236 } catch ( const std::exception &ex ) {
6237 SE_LOG_ERROR(NULL, NULL, "%s", ex.what());
6239 SE_LOG_ERROR(NULL, NULL, "unknown error");