Imported Upstream version 1.1
[platform/upstream/syncevolution.git] / src / syncevo-dbus-server.cpp
1 /*
2  * Copyright (C) 2009 Intel Corporation
3  *
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.
8  *
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.
13  *
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
17  * 02110-1301  USA
18  */
19
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
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>
36
37 #include <synthesis/san.h>
38
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <signal.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <limits.h>
45 #include <sys/time.h>
46
47 #include <list>
48 #include <map>
49 #include <memory>
50 #include <iostream>
51 #include <limits>
52 #include <cmath>
53 #include <boost/shared_ptr.hpp>
54 #include <boost/weak_ptr.hpp>
55 #include <boost/noncopyable.hpp>
56
57 #include <glib-object.h>
58 #include <glib/gi18n.h>
59 #ifdef USE_GNOME_KEYRING
60 extern "C" {
61 #include <gnome-keyring.h>
62 }
63 #endif
64
65 #ifdef HAS_NOTIFY
66 #include <libnotify/notify.h>
67 #endif
68
69 class DBusMessage;
70 static DBusMessage *SyncEvoHandleException(DBusMessage *msg);
71 #define DBUS_CXX_EXCEPTION_HANDLER SyncEvoHandleException
72 #include "gdbus-cxx-bridge.h"
73
74 using namespace SyncEvo;
75
76 static GMainLoop *loop = NULL;
77 static bool shutdownRequested = false;
78 static LogRedirect *redirectPtr;
79
80 /**
81  * Anything that can be owned by a client, like a connection
82  * or session.
83  */
84 class Resource {
85 public:
86     virtual ~Resource() {}
87 };
88
89 class Session;
90 class Connection;
91 class Client;
92 class DBusTransportAgent;
93 class DBusUserInterface;
94 class DBusServer;
95
96 class DBusSyncException : public DBusCXXException, public Exception
97 {
98  public:
99     DBusSyncException(const std::string &file,
100                   int line,
101                   const std::string &what) : Exception(file, line, what)
102     {}
103     /**
104      * get exception name, used to convert to dbus error name
105      * subclasses should override it
106      */
107     virtual std::string getName() const { return "org.syncevolution.Exception"; }
108
109     virtual const char* getMessage() const { return Exception::what(); }
110 };
111
112 /**
113  * exceptions classes deriving from DBusException
114  * org.syncevolution.NoSuchConfig 
115  */
116 class NoSuchConfig: public DBusSyncException
117 {
118  public:
119     NoSuchConfig(const std::string &file,
120                  int line,
121                  const std::string &error): DBusSyncException(file, line, error)
122     {}
123     virtual std::string getName() const { return "org.syncevolution.NoSuchConfig";}
124 };
125
126 /**
127  * org.syncevolution.NoSuchSource 
128  */
129 class NoSuchSource : public DBusSyncException
130 {
131  public:
132     NoSuchSource(const std::string &file,
133                  int line,
134                  const std::string &error): DBusSyncException(file, line, error)
135     {}
136     virtual std::string getName() const { return "org.syncevolution.NoSuchSource";}
137 };
138
139 /**
140  * org.syncevolution.InvalidCall 
141  */
142 class InvalidCall : public DBusSyncException
143 {
144  public:
145     InvalidCall(const std::string &file,
146                  int line,
147                  const std::string &error): DBusSyncException(file, line, error)
148     {}
149     virtual std::string getName() const { return "org.syncevolution.InvalidCall";}
150 };
151
152 /**
153  * org.syncevolution.SourceUnusable
154  * CheckSource will use this when the source cannot be used for whatever reason 
155  */
156 class SourceUnusable : public DBusSyncException
157 {
158  public:
159     SourceUnusable(const std::string &file,
160                    int line,
161                    const std::string &error): DBusSyncException(file, line, error)
162     {}
163     virtual std::string getName() const { return "org.syncevolution.SourceUnusable";}
164 };
165
166 /**
167  * implement syncevolution exception handler
168  * to cover its default implementation
169  */
170 static DBusMessage* SyncEvoHandleException(DBusMessage *msg)
171 {
172     /** give an opportunity to let syncevolution handle exception */
173     Exception::handle();
174     try {
175         throw;
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());
182     } catch (...) {
183         return b_dbus_create_error(msg, "org.syncevolution.Exception", "unknown");
184     }
185 }
186
187 /**
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
193  * valid parameters.
194  */
195 class Timeout
196 {
197     guint m_tag;
198     boost::function<bool ()> m_callback;
199
200 public:
201     Timeout() :
202         m_tag(0)
203     {
204     }
205
206     ~Timeout()
207     {
208         if (m_tag) {
209             g_source_remove(m_tag);
210         }
211     }
212
213     /**
214      * call the callback at regular intervals until it returns false
215      */
216     void activate(int seconds,
217                   const boost::function<bool ()> &callback)
218     {
219         m_callback = callback;
220         m_tag = g_timeout_add_seconds(seconds, triggered, static_cast<gpointer>(this));
221         if (!m_tag) {
222             SE_THROW("g_timeout_add_seconds() failed");
223         }
224     }
225
226     /**
227      * stop calling the callback, drop callback
228      */
229     void deactivate()
230     {
231         if (m_tag) {
232             g_source_remove(m_tag);
233             m_tag = 0;
234         }
235         m_callback = 0;
236     }
237
238 private:       
239     static gboolean triggered(gpointer data)
240     {
241         Timeout *me = static_cast<Timeout *>(data);
242         return me->m_callback();
243     }
244 };
245
246 /**
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.
250  */
251 class ReadOperations
252 {
253 public:
254     const std::string m_configName;
255
256     DBusServer &m_server;
257
258     ReadOperations(const std::string &config_name, DBusServer &server);
259
260     /** the double dictionary used to represent configurations */
261     typedef std::map< std::string, StringMap > Config_t;
262
263     /** the array of reports filled by getReports() */
264     typedef std::vector< StringMap > Reports_t;
265
266     /** the array of databases used by getDatabases() */
267     typedef SyncSource::Database SourceDatabase;
268     typedef SyncSource::Databases SourceDatabases_t;
269
270     /** implementation of D-Bus GetConfigs() */
271     void getConfigs(bool getTemplates, std::vector<std::string> &configNames);
272
273     /** implementation of D-Bus GetConfig() for m_configName as server configuration */
274     void getConfig(bool getTemplate,
275                    Config_t &config);
276
277     /** implementation of D-Bus GetReports() for m_configName as server configuration */
278     void getReports(uint32_t start, uint32_t count,
279                     Reports_t &reports);
280
281     /** Session.CheckSource() */
282     void checkSource(const string &sourceName);
283
284     /** Session.GetDatabases() */
285     void getDatabases(const string &sourceName, SourceDatabases_t &databases);
286
287 private:
288     /** 
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
293      */
294     virtual bool setFilters(SyncConfig &config) { return false; }
295
296     /**
297      * utility method which constructs a SyncConfig which references a local configuration (never a template)
298      *
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)
302      */
303     boost::shared_ptr<DBusUserInterface> getLocalConfig(const std::string &configName, bool mustExist = true);
304 };
305
306 /**
307  * dbus_traits for SourceDatabase. Put it here for 
308  * avoiding polluting gxx-dbus-bridge.h
309  */
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> > > >{}; 
315
316 /**
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.
323  */
324 class AutoTerm {
325     int m_refs;
326     time_t m_interval;
327     guint m_checkSource;
328     time_t m_lastUsed;
329
330     /**
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.
334      */
335     static gboolean checkCallback(gpointer data) {
336         AutoTerm *at = static_cast<AutoTerm*>(data);
337         if (!at->m_refs) {
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);
344             } else {
345                 // check again later
346                 at->m_checkSource = g_timeout_add_seconds(at->m_lastUsed + at->m_interval - now,
347                                                           checkCallback,
348                                                           data);
349             }
350         }
351         // always remove the current timeout, its job is done
352         return FALSE;
353     }
354
355  public:
356     /**
357      * constructor
358      * If interval is less than 0, it means 'unlimited' and never terminate
359      */
360     AutoTerm(int interval) :
361         m_refs(0),
362         m_checkSource(0),
363         m_lastUsed(0)
364     {
365         if (interval <= 0) {
366             m_interval = 0;
367             // increasing reference counts prevents shutdown forever
368             ref();
369         } else {
370             m_interval = interval;
371         }
372         reset();
373     }
374
375     ~AutoTerm()
376     {
377         if (m_checkSource) {
378             g_source_remove(m_checkSource);
379         }
380     }
381
382     //increase the actives objects
383     void ref(int refs = 1) {  
384         m_refs += refs; 
385         reset();
386     }
387
388     //decrease the actives objects
389     void unref(int refs = 1) { 
390         m_refs -= refs; 
391         if(m_refs <= 0) {
392            m_refs = 0;
393         }
394         reset();
395     }
396
397     /**
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
401      * idle long enough.
402      */
403     void reset()
404     {
405         if (m_refs > 0) {
406             // in use, don't need timeout
407             if (m_checkSource) {
408                 g_source_remove(m_checkSource);
409                 m_checkSource = 0;
410             }
411         } else {
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,
418                                                       checkCallback,
419                                                       static_cast<gpointer>(this));
420             }
421         }
422     }
423 };
424
425 class InfoReq;
426
427 /**
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
434  *
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
440  *
441  * This class is to manage querying bluetooth devices from org.bluez. Also
442  * it acts a proxy to org.bluez.Manager.
443  */
444 class BluezManager : public DBusRemoteObject {
445 public:
446     BluezManager(DBusServer &server); 
447
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; }
453
454 private:
455     class BluezDevice;
456
457     /**
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
461      */
462     class BluezAdapter: public DBusRemoteObject
463     {
464      public:
465         BluezAdapter (BluezManager &manager, const string &path); 
466
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)
472         {
473             if(forceDone || m_devReplies >= m_devNo) {
474                 m_devReplies = m_devNo = 0;
475                 m_manager.setDone(true);
476             } else {
477                 m_manager.setDone(false);
478             }
479         }
480
481         std::vector<boost::shared_ptr<BluezDevice> >& getDevices() { return m_devices; }
482
483      private:
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);
486
487         /** callback of 'DeviceRemoved' signal. Used to track a device is removed */
488         void deviceRemoved(const DBusObject_t &object);
489
490         /** callback of 'DeviceCreated' signal. Used to track a new device is created */
491         void deviceCreated(const DBusObject_t &object);
492
493         BluezManager &m_manager;
494         /** the object path of adapter */
495         std::string m_path;
496         /** the number of device for the default adapter */
497         int m_devNo;
498         /** the number of devices having reply */
499         int m_devReplies;
500
501         /** all available devices */
502         std::vector<boost::shared_ptr<BluezDevice> > m_devices;
503
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;
508
509         friend class BluezDevice;
510     };
511
512     /**
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
516      */
517     class BluezDevice: public DBusRemoteObject
518     {
519      public:
520         typedef map<string, boost::variant<vector<string>, string > > PropDict;
521
522         BluezDevice (BluezAdapter &adapter, const string &path);
523
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; }
529
530         /**
531          * check whether the current device has sync service
532          * if yes, put it in the adapter's sync devices list
533          */
534         void checkSyncService(const std::vector<std::string> &uuids);
535
536      private:
537         /** callback of 'GetProperties' method. The properties of the device is gotten */
538         void getPropertiesCb(const PropDict &props, const string &error);
539
540         /** callback of 'PropertyChanged' signal. Changed property is tracked */
541         void propertyChanged(const string &name, const boost::variant<vector<string>, string> &prop);
542
543         BluezAdapter &m_adapter;
544         /** the object path of the device */
545         string m_path;
546         /** name of the device */
547         string m_name;
548         /** mac address of the device */
549         string m_mac;
550         /** whether the calling of 'GetProperties' is returned */
551         bool m_reply;
552
553         typedef SignalWatch2<string, boost::variant<vector<string>, string> > PropertySignal;
554         /** represents 'PropertyChanged' signal of org.bluez.Device */
555         PropertySignal m_propertyChanged;
556
557         friend class BluezAdapter;
558     };
559
560     /*
561      * check whether the data is generated. If errors, force initilization done
562      */
563     void setDone(bool done) { m_done = done; }
564
565     /** callback of 'DefaultAdapter' method to get the default bluetooth adapter  */
566     void defaultAdapterCb(const DBusObject_t &adapter, const string &error);
567
568     /** callback of 'DefaultAdapterChanged' signal to track changes of the default adapter */
569     void defaultAdapterChanged(const DBusObject_t &adapter);
570
571     DBusServer &m_server;
572     DBusConnectionPtr m_bluezConn;
573     boost::shared_ptr<BluezAdapter> m_adapter;
574
575     /** represents 'DefaultAdapterChanged' signal of org.bluez.Adapter*/
576     SignalWatch1<DBusObject_t> m_adapterChanged;
577
578     /** flag to indicate whether the calls are all returned */
579     bool m_done;
580 };
581
582 /**
583  * a listener to listen changes of session 
584  * currently only used to track changes of running a sync in a session
585  */
586 class SessionListener
587 {
588 public:
589     /**
590      * method is called when a sync is successfully started.
591      * Here 'successfully started' means the synthesis engine starts
592      * to access the sources.
593      */
594     virtual void syncSuccessStart() {}
595
596     /**
597      * method is called when a sync is done. Also
598      * sync status are passed.
599      */
600     virtual void syncDone(SyncMLStatus status) {}
601
602     virtual ~SessionListener() {}
603 };
604
605 /**
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. 
611  *
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.
615  *
616  * The DBusServer is in charge of dispatching requests from dbus clients and automatic sync tasks.
617  * See DBusServer::run().
618  *
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. 
623  */
624 class AutoSyncManager : public SessionListener
625 {
626     DBusServer &m_server;
627
628     /**
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.
638      */
639     class AutoSyncTask
640     {
641      public:
642         /** the peer name of a config */
643         string m_peer;
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. */
647         string m_url;
648
649         AutoSyncTask(const string &peer, unsigned int delay, const string &url)
650             : m_peer(peer), m_delay(delay), m_url(url)
651         {}
652
653         /** compare whether two tasks are the same. May refine it later with more information */
654         bool operator==(const AutoSyncTask &right) const
655         {
656             if(boost::iequals(m_peer, right.m_peer) &&
657                     boost::iequals(m_url, right.m_url)) {
658                 return true;
659             }
660             return false;
661         }
662     };
663
664     /**
665      * AutoSyncTaskList is used to manage sync tasks which are grouped by the
666      * interval. Each list has one timeout gsource.
667      */
668     class AutoSyncTaskList : public list<AutoSyncTask>
669     {
670         AutoSyncManager &m_manager;
671         /** the interval used to create timeout source (seconds) */
672         unsigned int m_interval;
673
674         /** timeout gsource */
675         GLibEvent m_source;
676
677         /** callback of timeout source */
678         static gboolean taskListTimeoutCb(gpointer data);
679
680      public:
681         AutoSyncTaskList(AutoSyncManager &manager, unsigned int interval)
682             : m_manager(manager), m_interval(interval), m_source(0) 
683         {}
684         ~AutoSyncTaskList() {
685             if(m_source) {
686                 g_source_remove(m_source);
687             }
688         }
689
690         /** create timeout source once all tasks are added */
691         void createTimeoutSource();
692
693         /** check task list and put task into working queue */
694         void scheduleTaskList();
695     };
696
697 #ifdef HAS_NOTIFY
698     /**
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.
703      */
704     class Notification
705     {
706     public:
707         Notification(); 
708         ~Notification();
709
710         /** callback of click button(actions) of notification */
711         static void notifyAction(NotifyNotification *notify, gchar *action, gpointer userData);
712
713         /**
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.
717          */
718         void send(const char *summary, const char *body, const char *viewParams = NULL);
719     private:
720         /** flag to indicate whether libnotify is initalized successfully */
721         bool m_init;
722
723         /** flag to indicate whether libnotify accepts actions */
724         bool m_actions;
725
726         /** the current notification */
727         NotifyNotification *m_notification;
728     };
729 #endif
730
731     /** init a config and set up auto sync task for it */
732     void initConfig(const string &configName);
733
734     /** remove tasks from m_peerMap and m_workQueue created from the config */
735     void remove(const string &configName);
736
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;
740     PeerMap m_peerMap;
741
742     /** 
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;
746
747     /**
748      * the current active task, which may own a session 
749      */
750     boost::shared_ptr<AutoSyncTask> m_activeTask;
751
752     /** 
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 
755      */
756     boost::shared_ptr<Session> m_session;
757
758     /** the current sync of session is successfully started */
759     bool m_syncSuccessStart;
760
761 #ifdef HAS_NOTIFY
762     /** used to send notifications */
763     Notification m_notify;
764 #endif
765
766     /** 
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
769      * auto sync tasks.
770      */
771     void init();
772
773     /** operations on tasks queue */
774     void clearAllTasks() { m_workQueue.clear(); }
775
776     /** check m_peerMap and put all tasks in it to working queue */
777     void scheduleAll();
778
779     /** 
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.
783      */
784     bool addTask(const AutoSyncTask &syncTask);
785
786     /** find an auto sync task in the working queue or is running */
787     bool findTask(const AutoSyncTask &syncTask);
788
789     /** 
790      * check whether a task is suitable to put in the working queue
791      * Manager has the information needed to make the decision
792      */
793     bool taskLikelyToRun(const AutoSyncTask &syncTask);
794
795  public:
796     AutoSyncManager(DBusServer &server)
797         : m_server(server), m_syncSuccessStart(false) 
798     { 
799         init();
800     }
801
802     /**
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.
806      */
807     bool preventTerm() { return !m_peerMap.empty(); }
808
809     /**
810      * called when a config is changed. This causes re-loading the config 
811      */
812     void update(const string &configName);
813
814     /* Is there any auto sync task in the queue? */
815     bool hasTask() { return !m_workQueue.empty(); }
816
817     /** 
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
822      */
823     void startTask();
824
825     /** check whether the active session is owned by Automatic Sync Manger */
826     bool hasActiveSession();
827
828     /** set config and run sync to make the session ready to run */
829     void prepare();
830
831     /**
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.
835      */
836     virtual void syncSuccessStart();
837     virtual void syncDone(SyncMLStatus status);
838 };
839
840 /**
841  * A timer helper to check whether now is timeout according to
842  * user's setting. Timeout is calculated in milliseconds
843  */ 
844 class Timer {
845     timeval m_startTime;  ///< start time
846     unsigned long m_timeoutMs; ///< timeout in milliseconds, set by user
847
848     /**
849      * calculate duration between now and start time
850      * return value is in milliseconds
851      */
852     unsigned long duration(const timeval &minuend, const timeval &subtrahend)
853     {
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;
858             result *= 1000;
859             result += (minuend.tv_usec - subtrahend.tv_usec) / 1000;
860         }
861         return result;
862     }
863
864  public:
865     /**
866      * constructor
867      * @param timeoutMs timeout in milliseconds
868      */
869     Timer(unsigned long timeoutMs = 0) : m_timeoutMs(timeoutMs)
870     {
871         reset();
872     }
873
874     /**
875      * reset the timer and mark start time as current time
876      */
877     void reset() { gettimeofday(&m_startTime, NULL); }
878
879     /**
880      * check whether it is timeout
881      */
882     bool timeout() 
883     {
884         return timeout(m_timeoutMs);
885     }
886
887     /** 
888      * check whether the duration timer records is longer than the given duration 
889      */
890     bool timeout(unsigned long timeoutMs)
891     {
892         timeval now;
893         gettimeofday(&now, NULL);
894         return duration(now, m_startTime) >= timeoutMs;
895     }
896 };
897
898 class PresenceStatus {
899     bool m_httpPresence;
900     bool m_btPresence;
901     bool m_initiated;
902     DBusServer &m_server;
903
904     /** two timers to record when the statuses of network and bt are changed */
905     Timer m_httpTimer;
906     Timer m_btTimer;
907
908     enum PeerStatus {
909         /* The transport is not available (local problem) */
910         NOTRANSPORT,
911         /* The peer is not contactable (remote problem) */
912         UNREACHABLE,
913         /* Not for sure whether the peer is presence but likely*/
914         MIGHTWORK,
915
916         INVALID
917     };
918
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;
922     StatusMap m_peers;
923
924     static std::string status2string (PeerStatus status) {
925         switch (status) {
926             case NOTRANSPORT:
927                 return "no transport";
928                 break;
929             case UNREACHABLE:
930                 return "not present";
931                 break;
932             case MIGHTWORK:
933                 return "";
934                 break;
935             case INVALID:
936                 return "invalid transport status";
937         }
938         // not reached, keep compiler happy
939         return "";
940     }
941
942     public:
943     PresenceStatus (DBusServer &server)
944         :m_httpPresence (false), m_btPresence (false), m_initiated (false), m_server (server),
945         m_httpTimer(), m_btTimer()
946     {
947     }
948
949     enum TransportType{
950         HTTP_TRANSPORT,
951         BT_TRANSPORT,
952         INVALID_TRANSPORT
953     };
954
955     void init();
956
957     /* Implement DBusServer::checkPresence*/
958     void checkPresence (const string &peer, string& status, std::vector<std::string> &transport); 
959
960     void updateConfigPeers (const std::string &peer, const ReadOperations::Config_t &config);
961
962     void updatePresenceStatus (bool httpPresence, bool btPresence); 
963     void updatePresenceStatus (bool newStatus, TransportType type);
964
965     bool getHttpPresence() { return m_httpPresence; }
966     bool getBtPresence() { return m_btPresence; }
967     Timer& getHttpTimer() { return m_httpTimer; }
968     Timer& getBtTimer() { return m_btTimer; }
969 };
970
971 /*
972  * Implements org.moblin.connman.Manager
973  * GetProperty  : getPropCb
974  * PropertyChanged: propertyChanged
975  **/
976 class ConnmanClient : public DBusRemoteObject
977 {
978 public:
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();}
984
985     void propertyChanged(const string &name,
986                          const boost::variant<vector<string>, string> &prop);
987
988     void getPropCb(const std::map <std::string, boost::variant <std::vector <std::string> > >& props, const string &error);
989
990 private:
991     DBusServer &m_server;
992     DBusConnectionPtr m_connmanConn;
993
994     SignalWatch2 <string,boost::variant<vector<string>, string> > m_propertyChanged;
995 };
996
997
998
999 /**
1000  * Implements the main org.syncevolution.Server interface.
1001  *
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.
1007  */
1008 class DBusServer : public DBusObjectHelper,
1009                    public LoggerBase
1010 {
1011     GMainLoop *m_loop;
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;
1015
1016     /* Event source that regurally pool network manager
1017      * */
1018     GLibEvent m_pollConnman;
1019     /**
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.
1024      *
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).
1028      */
1029     Session *m_activeSession;
1030
1031     /**
1032      * The weak pointer that corresponds to m_activeSession.
1033      */
1034     boost::weak_ptr<Session> m_activeSessionRef;
1035
1036     /**
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.
1040      */
1041     boost::shared_ptr<Session> m_syncSession;
1042
1043     typedef std::list< boost::weak_ptr<Session> > WorkQueue_t;
1044     /**
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)
1048      * automatic syncs.
1049      *
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).
1053      */
1054     WorkQueue_t m_workQueue;
1055
1056     /**
1057      * a hash of pending InfoRequest
1058      */
1059     typedef std::map<string, boost::weak_ptr<InfoReq> > InfoReqMap;
1060
1061     // hash map of pending info requests
1062     InfoReqMap m_infoReqMap;
1063
1064     // the index of last info request
1065     uint32_t m_lastInfoReq;
1066
1067     // a hash to represent matched templates for devices, the key is
1068     // the peer name
1069     typedef std::map<string, boost::shared_ptr<SyncConfig::TemplateDescription> > MatchedTemplates;
1070
1071     MatchedTemplates m_matchedTempls;
1072
1073     BluezManager m_bluezManager;
1074
1075     /** devices which have sync services */
1076     SyncConfig::DeviceList m_syncDevices;
1077
1078     /**
1079      * Watch callback for a specific client or connection.
1080      */
1081     void clientGone(Client *c);
1082
1083     /** Server.GetCapabilities() */
1084     vector<string> getCapabilities();
1085
1086     /** Server.GetVersions() */
1087     StringMap getVersions();
1088
1089     /** Server.Attach() */
1090     void attachClient(const Caller_t &caller,
1091                       const boost::shared_ptr<Watch> &watch);
1092
1093     /** Server.Detach() */
1094     void detachClient(const Caller_t &caller);
1095
1096     /** Server.DisableNotifications() */
1097     void disableNotifications(const Caller_t &caller,
1098                               const string &notifications) {
1099         setNotifications(false, caller, notifications);
1100     }
1101
1102     /** Server.EnableNotifications() */
1103     void enableNotifications(const Caller_t &caller,
1104                              const string &notifications) {
1105         setNotifications(true, caller, notifications);
1106     }
1107
1108     /** actual implementation of enable and disable */
1109     void setNotifications(bool enable,
1110                           const Caller_t &caller,
1111                           const string &notifications);
1112
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);
1120
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);
1127     }
1128
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);
1135
1136     /** Server.GetConfig() */
1137     void getConfig(const std::string &config_name,
1138                    bool getTemplate,
1139                    ReadOperations::Config_t &config)
1140     {
1141         ReadOperations ops(config_name, *this);
1142         ops.getConfig(getTemplate , config);
1143     }
1144
1145     /** Server.GetReports() */
1146     void getReports(const std::string &config_name,
1147                     uint32_t start, uint32_t count,
1148                     ReadOperations::Reports_t &reports)
1149     {
1150         ReadOperations ops(config_name, *this);
1151         ops.getReports(start, count, reports);
1152     }
1153
1154     /** Server.CheckSource() */
1155     void checkSource(const std::string &configName,
1156                      const std::string &sourceName)
1157     {
1158         ReadOperations ops(configName, *this);
1159         ops.checkSource(sourceName);
1160     }
1161
1162     /** Server.GetDatabases() */
1163     void getDatabases(const std::string &configName,
1164                       const string &sourceName,
1165                       ReadOperations::SourceDatabases_t &databases)
1166     {
1167         ReadOperations ops(configName, *this);
1168         ops.getDatabases(sourceName, databases);
1169     }
1170
1171     void getConfigs(bool getTemplates, 
1172                     std::vector<std::string> &configNames)
1173     {
1174         ReadOperations ops("", *this);
1175         ops.getConfigs(getTemplates, configNames);
1176     }
1177
1178     /** Server.CheckPresence() */
1179     void checkPresence(const std::string &server,
1180                        std::string &status,
1181                        std::vector<std::string> &transports);
1182
1183     /** Server.GetSessions() */
1184     void getSessions(std::vector<DBusObject_t> &sessions);
1185
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);
1191
1192     friend class InfoReq;
1193
1194     /** emit InfoRequest */
1195     void emitInfoReq(const InfoReq &);
1196
1197     /** get the next id of InfoRequest */
1198     std::string getNextInfoReq();
1199
1200     /** remove InfoReq from hash map */
1201     void removeInfoReq(const InfoReq &req);
1202
1203     /** Server.SessionChanged */
1204     EmitSignal2<const DBusObject_t &,
1205                 bool> sessionChanged;
1206
1207     /** Server.PresenceChanged */
1208     EmitSignal3<const std::string &,
1209                 const std::string &,
1210                 const std::string &> presence;
1211
1212     /**
1213      * Server.TemplatesChanged, triggered each time m_syncDevices, the
1214      * input for the templates, is changed
1215      */
1216     EmitSignal0 templatesChanged;
1217
1218     /**
1219      * Server.ConfigChanged, triggered each time a session ends
1220      * which modified its configuration
1221      */
1222     EmitSignal0 configChanged;
1223
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;
1231
1232     /** Server.LogOutput */
1233     EmitSignal3<const DBusObject_t &,
1234                 string,
1235                 const std::string &> logOutput;
1236
1237     friend class Session;
1238
1239     PresenceStatus m_presence;
1240     ConnmanClient m_connman;
1241
1242     /** manager to automatic sync */
1243     AutoSyncManager m_autoSync;
1244
1245     //automatic termination
1246     AutoTerm m_autoTerm;
1247
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;
1251
1252     /**
1253      * All active timeouts created by addTimeout().
1254      * Each timeout which requests to be not called
1255      * again will be removed from this list.
1256      */
1257     list< boost::shared_ptr<Timeout> > m_timeouts;
1258
1259     /**
1260      * called each time a timeout triggers,
1261      * removes those which are done
1262      */
1263     bool callTimeout(const boost::shared_ptr<Timeout> &timeout, const boost::function<bool ()> &callback);
1264
1265 public:
1266     DBusServer(GMainLoop *loop, const DBusConnectionPtr &conn, int duration);
1267     ~DBusServer();
1268
1269     /** access to the GMainLoop reference used by this DBusServer instance */
1270     GMainLoop *getLoop() { return m_loop; }
1271
1272     /** process D-Bus calls until the server is ready to quit */
1273     void run();
1274
1275     /**
1276      * look up client by its ID
1277      */
1278     boost::shared_ptr<Client> findClient(const Caller_t &ID);
1279
1280     /**
1281      * find client by its ID or create one anew
1282      */
1283     boost::shared_ptr<Client> addClient(const DBusConnectionPtr &conn,
1284                                         const Caller_t &ID,
1285                                         const boost::shared_ptr<Watch> &watch);
1286
1287     /** detach this resource from all clients which own it */
1288     void detach(Resource *resource);
1289
1290     /**
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
1294      * ready to run.
1295      */
1296     void enqueue(const boost::shared_ptr<Session> &session);
1297
1298     /**
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.
1302      */
1303     int killSessions(const std::string &peerDeviceID);
1304
1305     /**
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
1309      * that lock.
1310      */
1311     void dequeue(Session *session);
1312
1313     /**
1314      * Checks whether the server is ready to run another session
1315      * and if so, activates the first one in the queue.
1316      */
1317     void checkQueue();
1318
1319     /**
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
1323      * being called.
1324      */
1325     void addTimeout(const boost::function<bool ()> &callback,
1326                     int seconds);
1327
1328     boost::shared_ptr<InfoReq> createInfoReq(const string &type, 
1329                                              const std::map<string, string> &parameters,
1330                                              const Session *session);
1331     void autoTermRef(int counts = 1) { m_autoTerm.ref(counts); }
1332
1333     void autoTermUnref(int counts = 1) { m_autoTerm.unref(counts); }
1334
1335     /** callback to reset for auto termination checking */
1336     void autoTermCallback() { m_autoTerm.reset(); }
1337
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);
1340
1341     PresenceStatus& getPresenceStatus() {return m_presence;}
1342
1343     void clearPeerTempls() { m_matchedTempls.clear(); }
1344     void addPeerTempl(const string &templName, const boost::shared_ptr<SyncConfig::TemplateDescription> peerTempl);
1345
1346     boost::shared_ptr<SyncConfig::TemplateDescription> getPeerTempl(const string &peer);
1347
1348     /**
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.
1353      */
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);
1358     /** add a 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);
1364
1365     /** emit a presence signal */
1366     void emitPresence(const string &server, const string &status, const string &transport)
1367     { 
1368         presence(server, status, transport); 
1369     }
1370
1371     /**
1372      * Returns new unique session ID. Implemented with a running
1373      * counter. Checks for overflow, but not currently for active
1374      * sessions.
1375      */
1376     std::string getNextSession();
1377
1378     AutoSyncManager &getAutoSyncManager() { return m_autoSync; }
1379
1380     /**
1381      * false if any client requested suppression of notifications
1382      */
1383     bool notificationsEnabled();
1384
1385     /**
1386      * implement virtual method from LogStdout.
1387      * Not only print the message in the console
1388      * but also send them as signals to clients
1389      */
1390     virtual void messagev(Level level,
1391                           const char *prefix,
1392                           const char *file,
1393                           int line,
1394                           const char *function,
1395                           const char *format,
1396                           va_list args);
1397 };
1398
1399 /**
1400  * Tracks a single client and all sessions and connections that it is
1401  * connected to. Referencing them ensures that they stay around as
1402  * long as needed.
1403  */
1404 class Client
1405 {
1406     DBusServer &m_server;
1407
1408     typedef std::list< boost::shared_ptr<Resource> > Resources_t;
1409     Resources_t m_resources;
1410
1411     /** counts how often a client has called Attach() without Detach() */
1412     int m_attachCount;
1413
1414     /** current client setting for notifications (see HAS_NOTIFY) */
1415     bool m_notificationsEnabled;
1416
1417     /** called 1 minute after last client detached from a session */
1418     static bool sessionExpired(const boost::shared_ptr<Session> &session);
1419
1420 public:
1421     const Caller_t m_ID;
1422
1423     Client(DBusServer &server,
1424            const Caller_t &ID) :
1425         m_server(server),
1426         m_attachCount(0),
1427         m_notificationsEnabled(true),
1428         m_ID(ID)
1429     {}
1430     ~Client();
1431
1432     void increaseAttachCount() { ++m_attachCount; }
1433     void decreaseAttachCount() { --m_attachCount; }
1434     int getAttachCount() const { return m_attachCount; }
1435
1436     void setNotificationsEnabled(bool enabled) { m_notificationsEnabled = enabled; }
1437     bool getNotificationsEnabled() const { return m_notificationsEnabled; }
1438
1439     /**
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.
1444      */
1445     void attach(boost::shared_ptr<Resource> resource)
1446     {
1447         m_resources.push_back(resource);
1448     }
1449
1450     /**
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
1454      * attach().
1455      */
1456     void detach(Resource *resource);
1457
1458     void detach(boost::shared_ptr<Resource> resource)
1459     {
1460         detach(resource.get());
1461     }
1462
1463     /**
1464      * Remove all references to the given resource, regardless whether
1465      * it was referenced not at all or multiple times.
1466      */
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);
1472             } else {
1473                 ++it;
1474             }
1475         }
1476     }
1477     void detachAll(boost::shared_ptr<Resource> resource)
1478     {
1479         detachAll(resource.get());
1480     }
1481
1482     /**
1483      * return corresponding smart pointer for a certain resource,
1484      * empty pointer if not found
1485      */
1486     boost::shared_ptr<Resource> findResource(Resource *resource)
1487     {
1488         for (Resources_t::iterator it = m_resources.begin();
1489              it != m_resources.end();
1490              ++it) {
1491             if (it->get() == resource) {
1492                 // got it
1493                 return *it;
1494             }
1495         }
1496         return boost::shared_ptr<Resource>();
1497     }
1498 };
1499
1500 struct SourceStatus
1501 {
1502     SourceStatus() :
1503         m_mode("none"),
1504         m_status("idle"),
1505         m_error(0)
1506     {}
1507     void set(const std::string &mode, const std::string &status, uint32_t error)
1508     {
1509         m_mode = mode;
1510         m_status = status;
1511         m_error = error;
1512     }
1513
1514     std::string m_mode;
1515     std::string m_status;
1516     uint32_t m_error;
1517 };
1518
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> > > >
1524 {};
1525
1526 struct SourceProgress
1527 {
1528     SourceProgress() :
1529         m_phase(""),
1530         m_prepareCount(-1), m_prepareTotal(-1),
1531         m_sendCount(-1), m_sendTotal(-1),
1532         m_receiveCount(-1), m_receiveTotal(-1)
1533     {}
1534
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;
1539 };
1540
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> > > > > > > >
1550 {};
1551
1552 /**
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.
1556  */
1557 class DBusUserInterface : public SyncContext
1558 {
1559 public:
1560     DBusUserInterface(const std::string &config);
1561
1562     /*
1563      * Ask password from gnome keyring, if not found, empty string
1564      * is returned
1565      */
1566     string askPassword(const string &passwordName, 
1567                        const string &descr, 
1568                        const ConfigPasswordKey &key);
1569
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);
1574
1575     /**
1576      * Read stdin via InfoRequest/Response.
1577      */
1578     void readStdin(string &content);
1579 };
1580
1581 /**
1582  * A running sync engine which keeps answering on D-Bus whenever
1583  * possible and updates the Session while the sync runs.
1584  */
1585 class DBusSync : public DBusUserInterface 
1586 {
1587     Session &m_session;
1588
1589 public:
1590     DBusSync(const std::string &config,
1591              Session &session);
1592     ~DBusSync() {}
1593
1594 protected:
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,
1599                                        SyncSource &source,
1600                                        int32_t extra1, int32_t extra2, int32_t extra3);
1601
1602     virtual void reportStepCmd(sysync::uInt16 stepCmd);
1603
1604     /** called when a sync is successfully started */
1605     virtual void syncSuccessStart(); 
1606
1607     /**
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.
1613      */
1614     virtual bool checkForSuspend(); 
1615     virtual bool checkForAbort();
1616     virtual int sleep(int intervals);
1617
1618     /**
1619      * Implement askPassword to retrieve password in gnome-keyring.
1620      * If not found, then ask it from dbus clients. 
1621      */
1622     string askPassword(const string &passwordName, 
1623                        const string &descr, 
1624                        const ConfigPasswordKey &key);
1625 };
1626
1627 /**
1628  * Hold progress info and try to estimate current progress
1629  */
1630 class ProgressData {
1631 public:
1632     /**
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
1637      */
1638     enum ProgressStep {
1639         /** an invalid step */
1640         PRO_SYNC_INVALID = 0,
1641         /** 
1642          * sync prepare step: do some preparations and checkings, 
1643          * such as source preparation, engine preparation
1644          */
1645         PRO_SYNC_PREPARE,
1646         /** 
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
1652          */
1653         PRO_SYNC_INIT,
1654         /** 
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 
1657          * much data.
1658          * assume 5 items to be sent by default
1659          */
1660         PRO_SYNC_DATA,
1661         /** 
1662          * item receive handling, send client's status to server and 
1663          * close the session 
1664          * assume 5 items to be received by default
1665          */
1666         PRO_SYNC_UNINIT,
1667         /** number of sync steps */
1668         PRO_SYNC_TOTAL
1669     };
1670     /**
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. 
1675      */
1676     enum InternalMode {
1677         INTERNAL_NONE = 0,
1678         INTERNAL_ONLY_TO_CLIENT = 1,
1679         INTERNAL_ONLY_TO_SERVER = 1 << 1,
1680         INTERNAL_TWO_WAY = 1 + (1 << 1)
1681     };
1682
1683     /**
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 
1687      * standard unit.
1688      * These ratios might be dynamicall changed in the future.
1689      */
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;
1704
1705     ProgressData(int32_t &progress);
1706
1707     /**
1708      * change the big step 
1709      */
1710     void setStep(ProgressStep step); 
1711
1712     /**
1713      * calc progress when a message is sent
1714      */
1715     void sendStart();
1716
1717     /**
1718      * calc progress when a message is received from server 
1719      */
1720     void receiveEnd();
1721
1722     /** 
1723      * re-calc progress proportions according to syncmode hint
1724      * typically, if only refresh-from-client, then
1725      * client won't receive data items.
1726      */
1727     void addSyncMode(SyncMode mode);
1728
1729     /**
1730      * calc progress when data prepare for sending 
1731      */
1732     void itemPrepare();
1733
1734     /**
1735      * calc progress when a data item is received
1736      */
1737     void itemReceive(const string &source, int count, int total);
1738
1739 private:
1740
1741     /** update progress data */
1742     void updateProg(float ratio);
1743
1744     /** dynamically adapt the proportion of each step by their current units */
1745     void recalc();
1746
1747     /** internally check sync mode */
1748     void checkInternalMode();
1749
1750     /** get total units of current step and remaining steps */
1751     float getRemainTotalUnits();
1752
1753     /** get default units of given step */
1754     static float getDefaultUnits(ProgressStep step);
1755
1756 private:
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 */
1762     int m_sendCounts;
1763     /** internal sync mode combinations */ 
1764     int m_internalMode;
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 */
1770     float m_propOfUnit;
1771     /** current sync source */
1772     string m_source;
1773 };
1774
1775 class CmdlineWrapper;
1776
1777 /**
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.
1781  */
1782 class Session : public DBusObjectHelper,
1783                 public Resource,
1784                 private ReadOperations,
1785                 private boost::noncopyable
1786 {
1787     DBusServer &m_server;
1788     std::vector<std::string> m_flags;
1789     const std::string m_sessionID;
1790     std::string m_peerDeviceID;
1791
1792     bool m_serverMode;
1793     SharedBuffer m_initialMessage;
1794     string m_initialMessageType;
1795
1796     boost::weak_ptr<Connection> m_connection;
1797     std::string m_connectionError;
1798     bool m_useConnection;
1799
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;
1805
1806     /** whether dbus clients set temporary configs */
1807     bool m_tempConfig;
1808
1809     /**
1810      * whether the dbus clients updated, removed or cleared configs,
1811      * ignoring temporary configuration changes
1812      */
1813     bool m_setConfig;
1814
1815     /**
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
1819      * pointer.
1820      */
1821     bool m_active;
1822
1823     /**
1824      * True once done() was called.
1825      */
1826     bool m_done;
1827
1828     /**
1829      * Indicates whether this session was initiated by the peer or locally.
1830      */
1831     bool m_remoteInitiated;
1832
1833     /**
1834      * The SyncEvolution instance which currently prepares or runs a sync.
1835      */
1836     boost::shared_ptr<DBusSync> m_sync;
1837
1838     /**
1839      * the sync status for session
1840      */
1841     enum SyncStatus {
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
1848         SYNC_ILLEGAL 
1849     };
1850
1851     /** current sync status */
1852     SyncStatus m_syncStatus;
1853
1854     /** step info: whether engine is waiting for something */
1855     bool m_stepIsWaiting;
1856
1857     /**
1858      * Priority which determines position in queue.
1859      * Lower is more important. PRI_DEFAULT is zero.
1860      */
1861     int m_priority;
1862
1863     int32_t m_progress;
1864
1865     /** progress data, holding progress calculation related info */
1866     ProgressData m_progData;
1867
1868     typedef std::map<std::string, SourceStatus> SourceStatuses_t;
1869     SourceStatuses_t m_sourceStatus;
1870
1871     uint32_t m_error;
1872     typedef std::map<std::string, SourceProgress> SourceProgresses_t;
1873     SourceProgresses_t m_sourceProgress;
1874
1875     /** timer for fire status/progress usages */
1876     Timer m_statusTimer;
1877     Timer m_progressTimer;
1878
1879     /** restore used */
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;
1886
1887     enum RunOperation {
1888         OP_SYNC = 0,
1889         OP_RESTORE = 1,
1890         OP_CMDLINE = 2,
1891         OP_NULL
1892     };
1893
1894     static string runOpToString(RunOperation op);
1895
1896     RunOperation m_runOperation;
1897
1898     /** listener to listen to changes of sync */
1899     SessionListener *m_listener;
1900
1901     /** Cmdline to execute command line args */
1902     boost::shared_ptr<CmdlineWrapper> m_cmdline;
1903
1904     /** Session.Attach() */
1905     void attach(const Caller_t &caller);
1906
1907     /** Session.Detach() */
1908     void detach(const Caller_t &caller);
1909
1910     /** Session.GetStatus() */
1911     void getStatus(std::string &status,
1912                    uint32_t &error,
1913                    SourceStatuses_t &sources);
1914     /** Session.GetProgress() */
1915     void getProgress(int32_t &progress,
1916                      SourceProgresses_t &sources);
1917
1918     /** Session.Restore() */
1919     void restore(const string &dir, bool before,const std::vector<std::string> &sources);
1920
1921     /** Session.checkPresence() */
1922     void checkPresence (string &status);
1923
1924     /** Session.Execute() */
1925     void execute(const vector<string> &args, const map<string, string> &vars);
1926
1927     /**
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.
1931      *
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.
1935      *
1936      * @param flush      force sending the current status
1937      */
1938     void fireStatus(bool flush = false);
1939     /** like fireStatus() for progress information */
1940     void fireProgress(bool flush = false);
1941
1942     /** Session.StatusChanged */
1943     EmitSignal3<const std::string &,
1944                 uint32_t,
1945                 const SourceStatuses_t &> emitStatus;
1946     /** Session.ProgressChanged */
1947     EmitSignal2<int32_t,
1948                 const SourceProgresses_t &> emitProgress;
1949
1950     static string syncStatusToString(SyncStatus state);
1951
1952 public:
1953     /**
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
1959      * needed.
1960      */
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>());
1966
1967     /**
1968      * automatically marks the session as completed before deleting it
1969      */
1970     ~Session();
1971
1972     /** explicitly mark the session as completed, even if it doesn't get deleted yet */
1973     void done();
1974
1975 private:
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;
1982
1983 public:
1984     enum {
1985         PRI_CMDLINE = -10,
1986         PRI_DEFAULT = 0,
1987         PRI_CONNECTION = 10,
1988         PRI_AUTOSYNC = 20
1989     };
1990
1991     /**
1992      * Default priority is 0. Higher means less important.
1993      */
1994     void setPriority(int priority) { m_priority = priority; }
1995     int getPriority() const { return m_priority; }
1996
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; }
2001
2002     /**
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.
2007      *
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
2012      * closes.
2013      */
2014     void setConnectionError(const std::string error) { m_connectionError = error; }
2015     std::string getConnectionError() { return m_connectionError; }
2016
2017
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; }
2022
2023     /**
2024      * TRUE if the session is ready to take over control
2025      */
2026     bool readyToRun() { return (m_syncStatus != SYNC_DONE) && (m_runOperation != OP_NULL); }
2027
2028     /**
2029      * transfer control to the session for the duration of the sync,
2030      * returns when the sync is done (successfully or unsuccessfully)
2031      */
2032     void run();
2033
2034     /**
2035      * called when the session is ready to run (true) or
2036      * lost the right to make changes (false)
2037      */
2038     void setActive(bool active);
2039
2040     bool getActive() { return m_active; }
2041
2042     void syncProgress(sysync::TProgressEventEnum type,
2043                       int32_t extra1, int32_t extra2, int32_t extra3);
2044     void sourceProgress(sysync::TProgressEventEnum type,
2045                         SyncSource &source,
2046                         int32_t extra1, int32_t extra2, int32_t extra3);
2047     string askPassword(const string &passwordName, 
2048                        const string &descr, 
2049                        const ConfigPasswordKey &key);
2050
2051     /** Session.GetFlags() */
2052     std::vector<std::string> getFlags() { return m_flags; }
2053
2054     /** Session.GetConfigName() */
2055     std::string getNormalConfigName() { return SyncConfig::normalizeConfigString(m_configName); }
2056
2057     /** Session.SetConfig() */
2058     void setConfig(bool update, bool temporary,
2059                    const ReadOperations::Config_t &config);
2060
2061     typedef StringMap SourceModes_t;
2062     /** Session.Sync() */
2063     void sync(const std::string &mode, const SourceModes_t &source_modes);
2064     /** Session.Abort() */
2065     void abort();
2066     /** Session.Suspend() */
2067     void suspend();
2068
2069     bool isSuspend() { return m_syncStatus == SYNC_SUSPEND; }
2070     bool isAbort() { return m_syncStatus == SYNC_ABORT; }
2071
2072     /**
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.
2076      */
2077     void setStepInfo(bool isWaiting); 
2078
2079     /** sync is successfully started */
2080     void syncSuccessStart();
2081
2082     /**
2083      * add a listener of the session. Old set listener is returned
2084      */
2085     SessionListener* addListener(SessionListener *listener);
2086
2087     void setRemoteInitiated (bool remote) { m_remoteInitiated = remote;}
2088 private:
2089     /** set m_syncFilter and m_sourceFilters to config */
2090     virtual bool setFilters(SyncConfig &config);
2091 };
2092
2093 /**
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.
2097  */
2098 class CmdlineWrapper
2099 {
2100     /**
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.
2106      */
2107     class CmdlineStreamBuf : public std::streambuf
2108     {
2109     public:
2110         virtual ~CmdlineStreamBuf()
2111         {
2112             //flush cached characters
2113             if(!m_str.empty()) {
2114                 SE_LOG(LoggerBase::SHOW, NULL, NULL, "%s", m_str.c_str());
2115             }
2116         }
2117     protected:
2118         /**
2119          * inherit from std::streambuf, all characters are cached in m_str
2120          * until a character '\n' is reached.
2121          */
2122         virtual int_type overflow (int_type ch) {
2123             if(ch == '\n') {
2124                 //don't append this character for logging system will append it
2125                 SE_LOG(LoggerBase::SHOW, NULL, NULL, "%s", m_str.c_str());
2126                 m_str.clear();
2127             } else if (ch != EOF) {
2128                 m_str += ch;
2129             }
2130             return ch;
2131         }
2132
2133         /** the cached output characters */
2134         string m_str;
2135     };
2136
2137     /** streambuf used for m_cmdlineOutStream */
2138     CmdlineStreamBuf m_outStreamBuf;
2139
2140     /** stream for command line out and err arguments */
2141     std::ostream m_cmdlineOutStream;
2142
2143     /**
2144      * implement factory method to create DBusSync instances
2145      * This can check 'abort' and 'suspend' command from clients.
2146      */
2147     class DBusCmdline : public Cmdline {
2148         Session &m_session;
2149     public:
2150         DBusCmdline(Session &session,
2151                     const vector<string> &args,
2152                     ostream &out,
2153                     ostream &err)
2154             :Cmdline(args, out, err), m_session(session)
2155         {}
2156
2157         SyncContext* createSyncClient() {
2158             return new DBusSync(m_server, m_session);
2159         }
2160     };
2161
2162     /** instance to run command line arguments */
2163     DBusCmdline m_cmdline;
2164
2165     /** environment variables passed from client */
2166     map<string, string> m_envVars;
2167
2168 public:
2169     /**
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.
2174      */
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),
2180         m_envVars(vars)
2181     {}
2182
2183     bool parse() { return m_cmdline.parse(); }
2184     void run()
2185     {
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)));
2190         }
2191         // exceptions must be handled (= printed) before returning,
2192         // so that our client gets the output
2193         try {
2194             if (!m_cmdline.run()) {
2195                 SE_THROW_EXCEPTION(DBusSyncException, "command line execution failure");
2196             }
2197
2198         } catch (...) {
2199             redirectPtr->flush();
2200             throw;
2201         }
2202         // always forward all currently pending redirected output
2203         // before closing the session
2204         redirectPtr->flush();
2205     }
2206
2207     bool configWasModified() { return m_cmdline.configWasModified(); }
2208 };
2209
2210 /**
2211  * Represents and implements the Connection interface.
2212  *
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.
2216  *
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
2220  * supported.
2221  */
2222 class Connection : public DBusObjectHelper, public Resource
2223 {
2224     DBusServer &m_server;
2225     StringMap m_peer;
2226     bool m_mustAuthenticate;
2227     enum {
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 */
2234     } m_state;
2235     std::string m_failure;
2236
2237     /** first parameter for Session::sync() */
2238     std::string m_syncMode;
2239     /** second parameter for Session::sync() */
2240     Session::SourceModes_t m_sourceModes;
2241
2242     const std::string m_sessionID;
2243     boost::shared_ptr<Session> m_session;
2244
2245     /**
2246      * main loop that our DBusTransportAgent is currently waiting in,
2247      * NULL if not waiting
2248      */
2249     GMainLoop *m_loop;
2250
2251     /**
2252      * get our peer session out of the DBusTransportAgent,
2253      * if it is currently waiting for us (indicated via m_loop)
2254      */
2255     void wakeupSession();
2256
2257     /**
2258      * buffer for received data, waiting here for engine to ask
2259      * for it via DBusTransportAgent::getReply().
2260      */
2261     SharedBuffer m_incomingMsg;
2262     std::string m_incomingMsgType;
2263
2264     struct SANContent {
2265         std::vector <string> m_syncType;
2266         std::vector <uint32_t> m_contentType;
2267         std::vector <string> m_serverURI;
2268     };
2269
2270     /**
2271      * The content of a parsed SAN package to be processed via
2272      * connection.ready
2273      */
2274     boost::shared_ptr <SANContent> m_SANContent;
2275     std::string m_peerBtAddr;
2276
2277     /**
2278      * records the reason for the failure, sends Abort signal and puts
2279      * the connection into the FAILED state.
2280      */
2281     void failed(const std::string &reason);
2282
2283     /**
2284      * returns "<description> (<ID> via <transport> <transport_description>)"
2285      */
2286     static std::string buildDescription(const StringMap &peer);
2287
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,
2294                bool normal,
2295                const std::string &error);
2296     /** wrapper around sendAbort */
2297     void abort();
2298     /** Connection.Abort */
2299     EmitSignal0 sendAbort;
2300     bool m_abortSent;
2301     /** Connection.Reply */
2302     EmitSignal5<const std::pair<size_t, const uint8_t *> &,
2303                 const std::string &,
2304                 const StringMap &,
2305                 bool,
2306                 const std::string &> reply;
2307
2308     friend class DBusTransportAgent;
2309
2310 public:
2311     const std::string m_description;
2312
2313     Connection(DBusServer &server,
2314                const DBusConnectionPtr &conn,
2315                const std::string &session_num,
2316                const StringMap &peer,
2317                bool must_authenticate);
2318
2319     ~Connection();
2320
2321     /** session requested by us is ready to run a sync */
2322     void ready();
2323
2324     /** connection is no longer needed, ensure that it gets deleted */
2325     void shutdown();
2326
2327     /** peer is not trusted, must authenticate as part of SyncML */
2328     bool mustAuthenticate() const { return m_mustAuthenticate; }
2329 };
2330
2331 /**
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.
2336  *
2337  * Reconnecting is not currently supported.
2338  */ 
2339 class DBusTransportAgent : public TransportAgent
2340 {
2341     GMainLoop *m_loop;
2342     Session &m_session;
2343     boost::weak_ptr<Connection> m_connection;
2344
2345     std::string m_url;
2346     std::string m_type;
2347
2348     /*
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.
2355      */
2356     TransportCallback m_callback;
2357     void *m_callbackData;
2358     int m_callbackInterval;
2359     GLibEvent m_eventSource;
2360     bool m_eventTriggered;
2361     bool m_waiting;
2362
2363     SharedBuffer m_incomingMsg;
2364     std::string m_incomingMsgType;
2365
2366     void doWait(boost::shared_ptr<Connection> &connection);
2367     static gboolean timeoutCallback(gpointer transport);
2368
2369  public:
2370     DBusTransportAgent(GMainLoop *loop,
2371                        Session &session,
2372                        boost::weak_ptr<Connection> connection);
2373     ~DBusTransportAgent();
2374
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)
2382     {
2383         m_callback = cb;
2384         m_callbackData = udata;
2385         m_callbackInterval = interval;
2386         m_eventSource = 0;
2387     }
2388     virtual void getReply(const char *&data, size_t &len, std::string &contentType);
2389 };
2390
2391 /**
2392  * A wrapper for handling info request and response.
2393  */
2394 class InfoReq {
2395 public:
2396     typedef std::map<string, string> InfoMap;
2397
2398     // status of current request
2399     enum Status {
2400         ST_RUN, // request is running
2401         ST_OK, // ok, response is gotten
2402         ST_TIMEOUT, // timeout
2403         ST_CANCEL // request is cancelled
2404     };
2405
2406     /**
2407      * constructor
2408      * The default timeout is 120 seconds
2409      */
2410     InfoReq(DBusServer &server,
2411             const string &type,
2412             const InfoMap &parameters,
2413             const Session *session,
2414             uint32_t timeout = 120); 
2415
2416     ~InfoReq();
2417
2418     /**
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
2422      */
2423     Status check();
2424
2425     /**
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
2432      */
2433     Status wait(InfoMap &response, uint32_t interval = 3);
2434
2435     /**
2436      * get response when it is ready. If false, nothing will be set in response
2437      */
2438     bool getResponse(InfoMap &response);
2439
2440     /** cancel the request. If request is done, cancel won't do anything */
2441     void cancel();
2442
2443     /** get current status in string format */
2444     string getStatusStr() const { return statusToString(m_status); }
2445
2446 private:
2447     static string statusToString(Status status);
2448
2449     enum InfoState {
2450         IN_REQ,  //request
2451         IN_WAIT, // waiting
2452         IN_DONE  // done
2453     };
2454
2455     static string infoStateToString(InfoState state);
2456
2457     /** callback for the timemout source */
2458     static gboolean checkCallback(gpointer data);
2459
2460     /** check whether the request is timeout */
2461     bool checkTimeout();
2462
2463     friend class DBusServer;
2464
2465     /** set response from dbus clients */
2466     void setResponse(const Caller_t &caller, const string &state, const InfoMap &response);
2467
2468     /** send 'done' state if needed */
2469     void done();
2470
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; }
2477
2478     DBusServer &m_server;
2479
2480     /** caller's session, might be NULL */
2481     const Session *m_session;
2482
2483     /** unique id of this info request */
2484     string m_id;
2485
2486     /** info req state defined in dbus api */
2487     InfoState m_infoState;
2488
2489     /** status to indicate the info request is timeout, ok, abort, etc */
2490     Status m_status;
2491
2492     /** the handler of the responsed dbus client */
2493     Caller_t m_handler;
2494
2495     /** the type of the info request */
2496     string m_type;
2497
2498     /** parameters from info request callers */
2499     InfoMap m_param;
2500
2501     /** response returned from dbus clients */
2502     InfoMap m_response;
2503
2504     /** default timeout is 120 seconds */
2505     uint32_t m_timeout;
2506
2507     /** a timer */
2508     Timer m_timer;
2509 };
2510
2511
2512 /***************** Client implementation ****************/
2513
2514 Client::~Client()
2515 {
2516     SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s is destructing", m_ID.c_str());
2517
2518     // explicitly detach all resources instead of just freeing the
2519     // list, so that the special behavior for sessions in detach() is
2520     // triggered
2521     while (!m_resources.empty()) {
2522         detach(m_resources.front().get());
2523     }
2524 }
2525
2526 bool Client::sessionExpired(const boost::shared_ptr<Session> &session)
2527 {
2528     SE_LOG_DEBUG(NULL, NULL, "session %s expired",
2529                  session->getSessionID().c_str());
2530     // don't call me again
2531     return false;
2532 }
2533
2534 void Client::detach(Resource *resource)
2535 {
2536     for (Resources_t::iterator it = m_resources.begin();
2537          it != m_resources.end();
2538          ++it) {
2539         if (it->get() == resource) {
2540             if (it->unique()) {
2541                 boost::shared_ptr<Session> session = boost::dynamic_pointer_cast<Session>(*it);
2542                 if (session) {
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
2547                     // session.
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,
2553                                                     session),
2554                                         60 /* 1 minute */);
2555
2556                     // allow other sessions to start
2557                     session->done();
2558                 }
2559             }
2560             // this will trigger removal of the resource if
2561             // the client was the last remaining owner
2562             m_resources.erase(it);
2563             return;
2564         }
2565     }
2566
2567     SE_THROW_EXCEPTION(InvalidCall, "cannot detach from resource that client is not attached to");
2568 }
2569
2570
2571 /***************** ReadOperations implementation ****************/
2572
2573 ReadOperations::ReadOperations(const std::string &config_name, DBusServer &server) :
2574     m_configName(config_name), m_server(server)
2575 {}
2576
2577 void ReadOperations::getConfigs(bool getTemplates, std::vector<std::string> &configNames)
2578 {
2579     if (getTemplates) {
2580         // get device list from dbus server, currently only bluetooth devices
2581         SyncConfig::DeviceList devices;
2582         m_server.getDeviceList(devices);
2583
2584         //clear existing templates in dbus server
2585         m_server.clearPeerTempls();
2586
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);
2593             } else {
2594                 string templName = "Bluetooth_";
2595                 templName += peer->m_deviceId;
2596                 templName += "_";
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));
2600                     templName += "1";
2601                 } else {
2602                     it->second++;
2603                     stringstream seq;
2604                     seq << it->second;
2605                     templName += seq.str();
2606                 }
2607                 configNames.push_back(templName);
2608                 m_server.addPeerTempl(templName, peer);
2609             }
2610         }
2611     } else {
2612         SyncConfig::ConfigList list = SyncConfig::getConfigs();
2613         BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server, list) {
2614             configNames.push_back(server.first);
2615         }
2616     }
2617 }
2618
2619 boost::shared_ptr<DBusUserInterface> ReadOperations::getLocalConfig(const string &configName, bool mustExist)
2620 {
2621     string peer, context;
2622     SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(configName),
2623                                   peer, context);
2624
2625     boost::shared_ptr<DBusUserInterface> syncConfig(new DBusUserInterface(configName));
2626
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 != "") &&
2632             mustExist &&
2633             !syncConfig->exists()) {
2634             SE_THROW_EXCEPTION(NoSuchConfig, "No configuration '" + configName + "' found");
2635         }
2636     }
2637     return syncConfig;
2638 }
2639
2640 void ReadOperations::getConfig(bool getTemplate,
2641                                Config_t &config)
2642 {
2643     map<string, string> localConfigs;
2644     boost::shared_ptr<SyncConfig> dbusConfig;
2645     boost::shared_ptr<DBusUserInterface> dbusUI;
2646     SyncConfig *syncConfig;
2647     string syncURL;
2648     /** get server template */
2649     if(getTemplate) {
2650         string peer, context;
2651
2652         boost::shared_ptr<SyncConfig::TemplateDescription> peerTemplate =
2653             m_server.getPeerTempl(m_configName);
2654         if(peerTemplate) {
2655             SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(peerTemplate->m_templateId),
2656                     peer, context);
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));
2660
2661             stringstream score;
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));
2671             }
2672
2673             // if the peer is client, then replace syncURL with bluetooth
2674             // MAC address
2675             syncURL = "obex-bt://";
2676             syncURL += peerTemplate->m_deviceId;
2677         } else {
2678             SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
2679                     peer, context);
2680             dbusConfig = SyncConfig::createPeerTemplate(peer);
2681         }
2682
2683         if(!dbusConfig.get()) {
2684             SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + m_configName + "' found");
2685         }
2686
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);
2690
2691         ConfigProps props;
2692         shared->getProperties()->readProperties(props);
2693         dbusConfig->setConfigFilter(true, "", props);
2694         BOOST_FOREACH(std::string source, shared->getSyncSources()) {
2695             SyncSourceNodes nodes = shared->getSyncSourceNodes(source, "");
2696             props.clear();
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
2701             // the other peers.
2702             props.erase("type");
2703             dbusConfig->setConfigFilter(false, source, props);
2704         }
2705         syncConfig = dbusConfig.get();
2706     } else {
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());
2712         }
2713         list<string> configuredSources = dbusUI->getSyncSources();
2714         BOOST_FOREACH(const string &sourceName, configuredSources) {
2715             ConfigPropertyRegistry& registry = SyncSourceConfig::getRegistry();
2716             SyncSourceNodes sourceNodes = dbusUI->getSyncSourceNodes(sourceName);
2717
2718             BOOST_FOREACH(const ConfigProperty *prop, registry) {
2719                 prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties(),
2720                         sourceName, sourceNodes.getProperties());
2721             }
2722         }
2723         syncConfig = dbusUI.get();
2724     }
2725
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));
2735         }
2736     }
2737
2738     // insert 'configName' of the chosen config (m_configName is not normalized)
2739     localConfigs.insert(pair<string, string>("configName", syncConfig->getConfigName()));
2740
2741     config.insert(pair<string,map<string, string> >("", localConfigs));
2742
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);
2752             if(!isDefault) {
2753                 localConfigs.insert(pair<string, string>(prop->getName(), value));
2754             }
2755         }
2756         config.insert(pair<string, map<string, string> >( "source/" + name, localConfigs));
2757     }
2758 }
2759
2760 void ReadOperations::getReports(uint32_t start, uint32_t count,
2761                                 Reports_t &reports)
2762 {
2763     SyncContext client(m_configName, false);
2764     std::vector<string> dirs;
2765     client.getSessions(dirs);
2766
2767     uint32_t index = 0;
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));
2776             SyncReport report;
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;
2784             }
2785
2786             /** serialize report to ConfigProps and then copy them to reports */
2787             HashFileConfigNode node("/dev/null","",true);
2788             node << report;
2789             ConfigProps props;
2790             node.readProperties(props);
2791
2792             BOOST_FOREACH(const ConfigProps::value_type &entry, props) {
2793                 aReport.insert(entry);
2794             }
2795             // a new key-value pair <"peer", [peer name]> is transferred
2796             aReport.insert(pair<string, string>("peer", peerName));
2797             reports.push_back(aReport);
2798         }
2799         index++;
2800     }
2801 }
2802
2803 void ReadOperations::checkSource(const std::string &sourceName)
2804 {
2805     boost::shared_ptr<SyncConfig> config(new SyncConfig(m_configName));
2806     setFilters(*config);
2807
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) {
2812             break;
2813         }
2814     }
2815     if(it == sourceNames.end()) {
2816         SE_THROW_EXCEPTION(NoSuchSource, "'" + m_configName + "' has no '" + sourceName + "' source");
2817     }
2818     bool checked = false;
2819     try {
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()));
2823
2824         if (syncSource.get()) {
2825             syncSource->open();
2826             // success!
2827             checked = true;
2828         }
2829     } catch (...) {
2830         Exception::handle();
2831     }
2832
2833     if (!checked) {
2834         SE_THROW_EXCEPTION(SourceUnusable, "The source '" + sourceName + "' is not usable");
2835     }
2836 }
2837 void ReadOperations::getDatabases(const string &sourceName, SourceDatabases_t &databases)
2838 {
2839     boost::shared_ptr<SyncConfig> config(new SyncConfig(m_configName));
2840     setFilters(*config);
2841
2842     SyncSourceParams params(sourceName, config->getSyncSourceNodes(sourceName));
2843     const SourceRegistry &registry(SyncSource::getSourceRegistry());
2844     BOOST_FOREACH(const RegisterSyncSource *sourceInfo, registry) {
2845         SyncSource *source = sourceInfo->m_create(params);
2846         if (!source) {
2847             continue;
2848         } else if (source == RegisterSyncSource::InactiveSource) {
2849             SE_THROW_EXCEPTION(NoSuchSource, "'" + m_configName + "' backend of source '" + sourceName + "' is not supported");
2850         } else {
2851             auto_ptr<SyncSource> autoSource(source);
2852             databases = autoSource->getDatabases();
2853             return;
2854         }
2855     }
2856
2857     SE_THROW_EXCEPTION(NoSuchSource, "'" + m_configName + "' has no '" + sourceName + "' source");
2858 }
2859
2860 /***************** DBusUserInterface implementation   **********************/
2861 DBusUserInterface::DBusUserInterface(const std::string &config):
2862     SyncContext(config, true)
2863 {
2864 }
2865
2866 inline const char *passwdStr(const std::string &str)
2867 {
2868     return str.empty() ? NULL : str.c_str();
2869 }
2870
2871 string DBusUserInterface::askPassword(const string &passwordName, 
2872                                       const string &descr, 
2873                                       const ConfigPasswordKey &key) 
2874 {
2875     string password;
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;
2882     GList* list;
2883
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),
2890                                                       key.port,
2891                                                       &list);
2892
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);
2899         return password;
2900     }
2901 #endif
2902     //if not found, return empty
2903     return "";
2904 }
2905
2906 bool DBusUserInterface::savePassword(const string &passwordName, 
2907                                      const string &password, 
2908                                      const ConfigPasswordKey &key)
2909 {
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 */
2913     guint32 itemId;
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),
2923                                                      key.port,
2924                                                      password.c_str(),
2925                                                      &itemId);
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));
2930 #else
2931         /** if gnome-keyring version is below 2.20, it doesn't support 'gnome_keyring_result_to_message'. */
2932         stringstream value;
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() + ".");
2935 #endif
2936     } 
2937     return true;
2938 #else
2939     /** if no support of gnome-keyring, don't save anything */
2940     return false;
2941 #endif
2942 }
2943
2944 void DBusUserInterface::readStdin(string &content)
2945 {
2946     throwError("reading stdin in D-Bus server not supported, use --daemon=no in command line");
2947 }
2948
2949 /***************** DBusSync implementation **********************/
2950
2951 DBusSync::DBusSync(const std::string &config,
2952                    Session &session) :
2953     DBusUserInterface(config),
2954     m_session(session)
2955 {
2956 }
2957
2958 boost::shared_ptr<TransportAgent> DBusSync::createTransportAgent()
2959 {
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(),
2963                                                                        m_session,
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();
2970         if (timeout) {
2971             agent->setCallback(transport_cb,
2972                                reinterpret_cast<void *>(static_cast<uintptr_t>(timeout)),
2973                                timeout);
2974         }
2975         return agent;
2976     } else {
2977         // no connection, use HTTP via libsoup/GMainLoop
2978         GMainLoop *loop = m_session.getServer().getLoop();
2979         boost::shared_ptr<TransportAgent> agent = SyncContext::createTransportAgent(loop);
2980         return agent;
2981     }
2982 }
2983
2984 void DBusSync::displaySyncProgress(sysync::TProgressEventEnum type,
2985                                    int32_t extra1, int32_t extra2, int32_t extra3)
2986 {
2987     SyncContext::displaySyncProgress(type, extra1, extra2, extra3);
2988     m_session.syncProgress(type, extra1, extra2, extra3);
2989 }
2990
2991 void DBusSync::displaySourceProgress(sysync::TProgressEventEnum type,
2992                                      SyncSource &source,
2993                                      int32_t extra1, int32_t extra2, int32_t extra3)
2994 {
2995     SyncContext::displaySourceProgress(type, source, extra1, extra2, extra3);
2996     m_session.sourceProgress(type, source, extra1, extra2, extra3);
2997 }
2998
2999 void DBusSync::reportStepCmd(sysync::uInt16 stepCmd)
3000 {
3001     switch(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);
3007             break;
3008         default:
3009             // otherwise, processing
3010             m_session.setStepInfo(false);
3011             break;
3012     }
3013 }
3014
3015 void DBusSync::syncSuccessStart()
3016 {
3017     m_session.syncSuccessStart();
3018 }
3019
3020 bool DBusSync::checkForSuspend()
3021 {
3022     return m_session.isSuspend() || SyncContext::checkForSuspend();
3023 }
3024
3025 bool DBusSync::checkForAbort()
3026 {
3027     return m_session.isAbort() || SyncContext::checkForAbort();
3028 }
3029
3030 int DBusSync::sleep(int intervals)
3031 {
3032     time_t start = time(NULL);
3033     while (true) {
3034         g_main_context_iteration(NULL, false);
3035         time_t now = time(NULL);
3036         if (checkForSuspend() || checkForAbort()) {
3037             return  (intervals - now + start);
3038         } 
3039         if (intervals - now + start <= 0) {
3040             return intervals - now +start;
3041         }
3042     }
3043 }
3044
3045 string DBusSync::askPassword(const string &passwordName, 
3046                              const string &descr, 
3047                              const ConfigPasswordKey &key) 
3048 {
3049     string password = DBusUserInterface::askPassword(passwordName, descr, key);
3050
3051     if(password.empty()) {
3052         password = m_session.askPassword(passwordName, descr, key);
3053     }
3054     return password;
3055 }
3056
3057 /***************** Session implementation ***********************/
3058
3059 void Session::attach(const Caller_t &caller)
3060 {
3061     boost::shared_ptr<Client> client(m_server.findClient(caller));
3062     if (!client) {
3063         throw runtime_error("unknown client");
3064     }
3065     boost::shared_ptr<Session> me = m_me.lock();
3066     if (!me) {
3067         throw runtime_error("session already deleted?!");
3068     }
3069     client->attach(me);
3070 }
3071
3072 void Session::detach(const Caller_t &caller)
3073 {
3074     boost::shared_ptr<Client> client(m_server.findClient(caller));
3075     if (!client) {
3076         throw runtime_error("unknown client");
3077     }
3078     client->detach(this);
3079 }
3080
3081 static void setSyncFilters(const ReadOperations::Config_t &config,FilterConfigNode::ConfigFilter &syncFilter,std::map<std::string, FilterConfigNode::ConfigFilter> &sourceFilters)
3082 {
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);
3089             }
3090         } else {
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);
3097                 }
3098             }
3099         }
3100     }
3101 }
3102 void Session::setConfig(bool update, bool temporary,
3103                         const ReadOperations::Config_t &config)
3104 {
3105     if (!m_active) {
3106         SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
3107     }
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);
3111     }
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");
3114     }
3115
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();
3122             m_setConfig = true;
3123         }
3124         return;
3125     }
3126     if(temporary) {
3127         /* save temporary configs in session filters */
3128         setSyncFilters(config, m_syncFilter, m_sourceFilters);
3129         m_tempConfig = true;
3130     } else {
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" );
3139         }
3140         if(!update) {
3141             list<string> sources = from->getSyncSources();
3142             list<string>::iterator it;
3143             for(it = sources.begin(); it != sources.end(); ++it) {
3144                 string source = "source/";
3145                 source += *it;
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);
3150                 } else {
3151                     /** just clear visiable properties, remove them and their values */
3152                     from->clearSyncSourceProperties(*it);
3153                 }
3154             }
3155             from->clearSyncProperties();
3156         }
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);
3163             }
3164         }
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);
3170         }
3171         boost::shared_ptr<DBusSync> syncConfig(new DBusSync(getConfigName(), *this));
3172         syncConfig->copy(*from, NULL);
3173
3174         syncConfig->preFlush(*syncConfig);
3175         syncConfig->flush();
3176         m_setConfig = true;
3177     }
3178 }
3179
3180 void Session::initServer(SharedBuffer data, const std::string &messageType)
3181 {
3182     m_serverMode = true;
3183     m_initialMessage = data;
3184     m_initialMessageType = messageType;
3185 }
3186
3187 void Session::sync(const std::string &mode, const SourceModes_t &source_modes)
3188 {
3189     if (!m_active) {
3190         SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
3191     }
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);
3198     }
3199
3200     m_sync.reset(new DBusSync(getConfigName(), *this));
3201     if (m_serverMode) {
3202         m_sync->initServer(m_sessionID,
3203                            m_initialMessage,
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"] = "";
3210         }
3211     }
3212
3213     if (m_remoteInitiated) {
3214         m_sync->setRemoteInitiated (true);
3215     }
3216
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;
3224     }
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;
3232         }
3233         m_sync->setConfigFilter(false, source, filter);
3234     }
3235
3236     // Update status and progress. From now on, all configured sources
3237     // have their default entry (referencing them by name creates the
3238     // entry).
3239     BOOST_FOREACH(const std::string source,
3240                   m_sync->getSyncSources()) {
3241         m_sourceStatus[source];
3242         m_sourceProgress[source];
3243     }
3244     fireProgress(true);
3245     fireStatus(true);
3246     m_runOperation = OP_SYNC;
3247
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);
3251 }
3252
3253 void Session::abort()
3254 {
3255     if (m_runOperation != OP_SYNC && m_runOperation != OP_CMDLINE) {
3256         SE_THROW_EXCEPTION(InvalidCall, "sync not started, cannot abort at this time");
3257     }
3258     m_syncStatus = SYNC_ABORT;
3259     fireStatus(true);
3260
3261     // state change, return to caller so that it can react
3262     g_main_loop_quit(m_server.getLoop());
3263 }
3264
3265 void Session::suspend()
3266 {
3267     if (m_runOperation != OP_SYNC && m_runOperation != OP_CMDLINE) {
3268         SE_THROW_EXCEPTION(InvalidCall, "sync not started, cannot suspend at this time");
3269     }
3270     m_syncStatus = SYNC_SUSPEND;
3271     fireStatus(true);
3272     g_main_loop_quit(m_server.getLoop());
3273 }
3274
3275 void Session::getStatus(std::string &status,
3276                         uint32_t &error,
3277                         SourceStatuses_t &sources)
3278 {
3279     status = syncStatusToString(m_syncStatus);
3280     if (m_stepIsWaiting) {
3281         status += ";waiting";
3282     }
3283
3284     error = m_error;
3285     sources = m_sourceStatus;
3286 }
3287
3288 void Session::getProgress(int32_t &progress,
3289                           SourceProgresses_t &sources)
3290 {
3291     progress = m_progress;
3292     sources = m_sourceProgress;
3293 }
3294
3295 void Session::fireStatus(bool flush)
3296 {
3297     std::string status;
3298     uint32_t error;
3299     SourceStatuses_t sources;
3300
3301     /** not force flushing and not timeout, return */
3302     if(!flush && !m_statusTimer.timeout()) {
3303         return;
3304     }
3305     m_statusTimer.reset();
3306
3307     getStatus(status, error, sources);
3308     emitStatus(status, error, sources);
3309 }
3310
3311 void Session::fireProgress(bool flush)
3312 {
3313     int32_t progress;
3314     SourceProgresses_t sources;
3315
3316     /** not force flushing and not timeout, return */
3317     if(!flush && !m_progressTimer.timeout()) {
3318         return;
3319     }
3320     m_progressTimer.reset();
3321
3322     getProgress(progress, sources);
3323     emitProgress(progress, sources);
3324 }
3325 string Session::syncStatusToString(SyncStatus state)
3326 {
3327     switch(state) {
3328     case SYNC_QUEUEING:
3329         return "queueing";
3330     case SYNC_IDLE:
3331         return "idle";
3332     case SYNC_RUNNING:
3333         return "running";
3334     case SYNC_ABORT:
3335         return "aborting";
3336     case SYNC_SUSPEND:
3337         return "suspending";
3338     case SYNC_DONE:
3339         return "done";
3340     default:
3341         return "";
3342     };
3343 }
3344
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)
3350 {
3351     boost::shared_ptr<Session> me(new Session(server, peerDeviceID, config_name, session, flags));
3352     me->m_me = me;
3353     return me;
3354 }
3355
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),
3366     m_server(server),
3367     m_flags(flags),
3368     m_sessionID(session),
3369     m_peerDeviceID(peerDeviceID),
3370     m_serverMode(false),
3371     m_useConnection(false),
3372     m_tempConfig(false),
3373     m_setConfig(false),
3374     m_active(false),
3375     m_done(false),
3376     m_remoteInitiated(false),
3377     m_syncStatus(SYNC_QUEUEING),
3378     m_stepIsWaiting(false),
3379     m_priority(PRI_DEFAULT),
3380     m_progress(0),
3381     m_progData(m_progress),
3382     m_error(0),
3383     m_statusTimer(100),
3384     m_progressTimer(50),
3385     m_restoreBefore(true),
3386     m_restoreSrcTotal(0),
3387     m_restoreSrcEnd(0),
3388     m_runOperation(OP_NULL),
3389     m_listener(NULL),
3390     emitStatus(*this, "StatusChanged"),
3391     emitProgress(*this, "ProgressChanged")
3392 {
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");
3411     add(emitStatus);
3412     add(emitProgress);
3413 }
3414
3415 void Session::done()
3416 {
3417     if (m_done) {
3418         return;
3419     }
3420
3421     /* update auto sync manager when a config is changed */
3422     if (m_setConfig) {
3423         m_server.getAutoSyncManager().update(m_configName);
3424     }
3425     m_server.dequeue(this);
3426
3427     // now tell other clients about config change?
3428     if (m_setConfig) {
3429         m_server.configChanged();
3430     }
3431
3432     // typically set by m_server.dequeue(), but let's really make sure...
3433     m_active = false;
3434
3435     m_done = true;
3436 }
3437
3438 Session::~Session()
3439 {
3440     done();
3441 }
3442
3443 void Session::setActive(bool active)
3444 {
3445     m_active = active;
3446     if (active) {
3447         if (m_syncStatus == SYNC_QUEUEING) {
3448             m_syncStatus = SYNC_IDLE;
3449             fireStatus(true);
3450         }
3451
3452         boost::shared_ptr<Connection> c = m_connection.lock();
3453         if (c) {
3454             c->ready();
3455         }
3456     }
3457 }
3458
3459 void Session::syncProgress(sysync::TProgressEventEnum type,
3460                            int32_t extra1, int32_t extra2, int32_t extra3)
3461 {
3462     switch(type) {
3463     case sysync::PEV_SESSIONSTART:
3464         m_progData.setStep(ProgressData::PRO_SYNC_INIT);
3465         fireProgress(true);
3466         break;
3467     case sysync::PEV_SESSIONEND:
3468         if((uint32_t)extra1 != m_error) {
3469             m_error = extra1;
3470             fireStatus(true);
3471         }
3472         m_progData.setStep(ProgressData::PRO_SYNC_INVALID);
3473         fireProgress(true);
3474         break;
3475     case sysync::PEV_SENDSTART:
3476         m_progData.sendStart();
3477         break;
3478     case sysync::PEV_SENDEND:
3479     case sysync::PEV_RECVSTART:
3480     case sysync::PEV_RECVEND:
3481         m_progData.receiveEnd();
3482         fireProgress();
3483         break;
3484     case sysync::PEV_DISPLAY100:
3485     case sysync::PEV_SUSPENDCHECK:
3486     case sysync::PEV_DELETING:
3487         break;
3488     case sysync::PEV_SUSPENDING:
3489         m_syncStatus = SYNC_SUSPEND;
3490         fireStatus(true);
3491         break;
3492     default:
3493         ;
3494     }
3495 }
3496
3497 void Session::sourceProgress(sysync::TProgressEventEnum type,
3498                              SyncSource &source,
3499                              int32_t extra1, int32_t extra2, int32_t extra3)
3500 {
3501     switch(m_runOperation) {
3502     case OP_SYNC: {
3503         SourceProgress &progress = m_sourceProgress[source.getName()];
3504         SourceStatus &status = m_sourceStatus[source.getName()];
3505         switch(type) {
3506         case sysync::PEV_SYNCSTART:
3507             if(source.getFinalSyncMode() != SYNC_NONE) {
3508                 m_progData.setStep(ProgressData::PRO_SYNC_UNINIT);
3509                 fireProgress();
3510             }
3511             break;
3512         case sysync::PEV_SYNCEND:
3513             if(source.getFinalSyncMode() != SYNC_NONE) {
3514                 status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "done", extra1);
3515                 fireStatus(true);
3516             }
3517             break;
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();
3524                 fireProgress(true);
3525             }
3526             break;
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;
3532                 fireProgress(true);
3533             }
3534             break;
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);
3541                 fireProgress(true);
3542             }
3543             break;
3544         case sysync::PEV_ALERTED:
3545             if(source.getFinalSyncMode() != SYNC_NONE) {
3546                 status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "running", 0);
3547                 fireStatus(true);
3548                 m_progData.setStep(ProgressData::PRO_SYNC_DATA);
3549                 m_progData.addSyncMode(source.getFinalSyncMode());
3550                 fireProgress();
3551             }
3552             break;
3553         default:
3554             ;
3555         }
3556         break;
3557     }
3558     case OP_RESTORE: {
3559         switch(type) {
3560         case sysync::PEV_ALERTED:
3561             // count the total number of sources to be restored
3562             m_restoreSrcTotal++;
3563             break;
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);
3569                 fireStatus(true);
3570             }
3571             break;
3572         }
3573         case sysync::PEV_SYNCEND: {
3574             if (source.getFinalSyncMode() != SYNC_NONE) {
3575                 m_restoreSrcEnd++;
3576                 SourceStatus &status = m_sourceStatus[source.getName()];
3577                 status.set(PrettyPrintSyncMode(source.getFinalSyncMode()), "done", 0);
3578                 m_progress = 100 * m_restoreSrcEnd / m_restoreSrcTotal;
3579                 fireStatus(true);
3580                 fireProgress(true);
3581             }
3582             break;
3583         }
3584         default:
3585             break;
3586         }
3587         break;
3588     }
3589     default:
3590         break;
3591     }
3592 }
3593
3594 void Session::run()
3595 {
3596     if (m_runOperation != OP_NULL) {
3597         try {
3598             m_syncStatus = SYNC_RUNNING;
3599             fireStatus(true);
3600             switch(m_runOperation) {
3601             case OP_SYNC: {
3602                 SyncMLStatus status;
3603                 m_progData.setStep(ProgressData::PRO_SYNC_PREPARE);
3604                 try {
3605                     status = m_sync->sync();
3606                 } catch (...) {
3607                     status = m_sync->handleException();
3608                 }
3609                 if (!m_error) {
3610                     m_error = status;
3611                 }
3612                 // if there is a connection, then it is no longer needed
3613                 boost::shared_ptr<Connection> c = m_connection.lock();
3614                 if (c) {
3615                     c->shutdown();
3616                 }
3617                 // report 'sync done' event to listener
3618                 if(m_listener) {
3619                     m_listener->syncDone(status);
3620                 }
3621                 break;
3622             }
3623             case OP_RESTORE:
3624                 m_sync->restore(m_restoreDir, 
3625                                 m_restoreBefore ? SyncContext::DATABASE_BEFORE_SYNC : SyncContext::DATABASE_AFTER_SYNC);
3626                 break;
3627             case OP_CMDLINE:
3628                 try {
3629                     m_cmdline->run();
3630                 } catch (...) {
3631                     SyncMLStatus status = Exception::handle();
3632                     if (!m_error) {
3633                         m_error = status;
3634                     }
3635                 }
3636                 m_setConfig = m_cmdline->configWasModified();
3637                 break;
3638             default:
3639                 break;
3640             };
3641         } catch (...) {
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;
3646             fireStatus(true);
3647             throw;
3648         }
3649         m_syncStatus = SYNC_DONE;
3650         m_stepIsWaiting = false;
3651         fireStatus(true);
3652     } 
3653 }
3654
3655 bool Session::setFilters(SyncConfig &config)
3656 {
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);
3662     }
3663     return m_tempConfig;
3664 }
3665
3666 void Session::setStepInfo(bool isWaiting)
3667 {
3668     // if stepInfo doesn't change, then ignore it to avoid duplicate status info
3669     if(m_stepIsWaiting != isWaiting) {
3670         m_stepIsWaiting = isWaiting;
3671         fireStatus(true);
3672     }
3673 }
3674
3675 void Session::restore(const string &dir, bool before, const std::vector<std::string> &sources)
3676 {
3677     if (!m_active) {
3678         SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
3679     }
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);
3688     }
3689
3690     m_sync.reset(new DBusSync(getConfigName(), *this));
3691
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);
3697         }
3698         // disable other sources
3699         FilterConfigNode::ConfigFilter disabled;
3700         disabled[SyncSourceConfig::m_sourcePropSync.getName()] = "disabled";
3701         m_sync->setConfigFilter(false, "", disabled);
3702     }
3703     m_restoreBefore = before;
3704     m_restoreDir = dir;
3705     m_runOperation = OP_RESTORE;
3706
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];
3711     }
3712     fireProgress(true);
3713     fireStatus(true);
3714
3715     g_main_loop_quit(loop);
3716 }
3717
3718 string Session::runOpToString(RunOperation op)
3719 {
3720     switch(op) {
3721     case OP_SYNC:
3722         return "sync";
3723     case OP_RESTORE:
3724         return "restore";
3725     case OP_CMDLINE:
3726         return "cmdline";
3727     default:
3728         return "";
3729     };
3730 }
3731
3732 void Session::execute(const vector<string> &args, const map<string, string> &vars)
3733 {
3734     if (!m_active) {
3735         SE_THROW_EXCEPTION(InvalidCall, "session is not active, call not allowed at this time");
3736     }
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);
3742     }
3743     //create ostream with a specified streambuf
3744     m_cmdline.reset(new CmdlineWrapper(*this, args, vars));
3745
3746     if(!m_cmdline->parse()) {
3747         m_cmdline.reset();
3748         SE_THROW_EXCEPTION(DBusSyncException, "arguments parsing error");
3749     }
3750
3751     m_runOperation = OP_CMDLINE;
3752     g_main_loop_quit(loop);
3753 }
3754
3755 inline void insertPair(std::map<string, string> &params,
3756                        const string &key, 
3757                        const string &value)
3758 {
3759     if(!value.empty()) {
3760         params.insert(pair<string, string>(key, value));
3761     }
3762 }
3763
3764 string Session::askPassword(const string &passwordName, 
3765                              const string &descr, 
3766                              const ConfigPasswordKey &key) 
3767 {
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));
3783         } else {
3784             return it->second;
3785         }
3786     } 
3787
3788     SE_THROW_EXCEPTION_STATUS(StatusException, "can't get the password from clients. The password request is '" + req->getStatusStr() + "'", STATUS_PASSWORD_TIMEOUT);
3789     return "";
3790 }
3791
3792 /*Implementation of Session.CheckPresence */
3793 void Session::checkPresence (string &status)
3794 {
3795     vector<string> transport;
3796     m_server.m_presence.checkPresence (m_configName, status, transport);
3797 }
3798
3799 void Session::syncSuccessStart()
3800 {
3801     // if listener, report 'sync started' to it
3802     if(m_listener) {
3803         m_listener->syncSuccessStart();
3804     }
3805 }
3806
3807 SessionListener* Session::addListener(SessionListener *listener)
3808 {
3809     SessionListener *old = m_listener;
3810     m_listener = listener;
3811     return old;
3812 }
3813
3814 /************************ ProgressData implementation *****************/
3815 ProgressData::ProgressData(int32_t &progress) 
3816     : m_progress(progress),
3817     m_step(PRO_SYNC_INVALID),
3818     m_sendCounts(0),
3819     m_internalMode(INTERNAL_NONE)
3820 {
3821     /**
3822      * init default units of each step 
3823      */
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;
3829     }
3830     m_propOfUnit = 1.0 / totalUnits;
3831
3832     /** 
3833      * init default sync step proportions. each step stores proportions of
3834      * its previous steps and itself.
3835      */
3836     m_syncProp[0] = 0;
3837     for(int i = 1; i < PRO_SYNC_TOTAL - 1; i++) {
3838         m_syncProp[i] = m_syncProp[i - 1] + m_syncUnits[i] / totalUnits;
3839     }
3840     m_syncProp[PRO_SYNC_TOTAL - 1] = 1.0;
3841 }
3842
3843 void ProgressData::setStep(ProgressStep step) 
3844 {
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
3851     }
3852 }
3853
3854 void ProgressData::sendStart()
3855 {
3856     checkInternalMode();
3857     m_sendCounts++;
3858
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;
3862         recalc();
3863     }
3864     /** 
3865      * If in the send operation of PRO_SYNC_UNINIT, it often takes extra time
3866      * to send message due to items handling 
3867      */
3868     if(m_step == PRO_SYNC_UNINIT && m_syncUnits[(int)m_step] != MSG_SEND_RECEIVE_TIMES) {
3869         updateProg(DATA_PREPARE_RATIO);
3870     }
3871 }
3872
3873 void ProgressData::receiveEnd()
3874 {
3875     /** 
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
3879      */
3880     updateProg(m_syncUnits[(int)m_step]);
3881 }
3882
3883 void ProgressData::addSyncMode(SyncMode mode)
3884 {
3885     switch(mode) {
3886         case SYNC_TWO_WAY:
3887         case SYNC_SLOW:
3888             m_internalMode |= INTERNAL_TWO_WAY;
3889             break;
3890         case SYNC_ONE_WAY_FROM_CLIENT:
3891         case SYNC_REFRESH_FROM_CLIENT:
3892             m_internalMode |= INTERNAL_ONLY_TO_CLIENT;
3893             break;
3894         case SYNC_ONE_WAY_FROM_SERVER:
3895         case SYNC_REFRESH_FROM_SERVER:
3896             m_internalMode |= INTERNAL_ONLY_TO_SERVER;
3897             break;
3898         default:
3899             ;
3900     };
3901 }
3902
3903 void ProgressData::itemPrepare()
3904 {
3905     checkInternalMode();
3906     /**
3907      * only the first PEV_ITEMPREPARE event takes some time
3908      * due to data access, other events don't according to
3909      * profiling data
3910      */
3911     if(m_source.empty()) {
3912         m_source = "source"; ///< use this to check whether itemPrepare occurs
3913         updateProg(DATA_PREPARE_RATIO);
3914     }
3915 }
3916
3917 void ProgressData::itemReceive(const string &source, int count, int total)
3918 {
3919     /** 
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
3923      */
3924     if(m_source.empty()) {
3925         m_source = source;
3926         if(total != 0) {
3927             m_syncUnits[PRO_SYNC_UNINIT] += ONEITEM_RECEIVE_RATIO * (total - DEFAULT_ITEMS);
3928             recalc();
3929         }
3930     /** if another new source, add them into sync units */
3931     } else if(m_source != source){
3932         m_source = source;
3933         if(total != 0) {
3934             m_syncUnits[PRO_SYNC_UNINIT] += ONEITEM_RECEIVE_RATIO * total;
3935             recalc();
3936         }
3937     } 
3938     updateProg(ONEITEM_RECEIVE_RATIO);
3939 }
3940
3941 void ProgressData::updateProg(float ratio)
3942 {
3943     m_progress += m_propOfUnit * 100 * ratio;
3944     m_syncUnits[(int)m_step] -= ratio;
3945 }
3946
3947 /** dynamically adapt the proportion of each step by their current units */
3948 void ProgressData::recalc()
3949 {
3950     float units = getRemainTotalUnits();
3951     if(std::abs(units) < std::numeric_limits<float>::epsilon()) {
3952         m_propOfUnit = 0.0;
3953     } else {
3954         m_propOfUnit = ( 100.0 - m_progress ) / (100.0 * units);
3955     }
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;
3960         }
3961     }
3962 }
3963
3964 void ProgressData::checkInternalMode() 
3965 {
3966     if(!m_internalMode) {
3967         return;
3968     } else if(m_internalMode & INTERNAL_TWO_WAY) {
3969         // don't adjust
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);
3973         recalc();
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);
3977         recalc();
3978     }
3979     m_internalMode = INTERNAL_NONE;
3980 }
3981
3982 float ProgressData::getRemainTotalUnits()
3983 {
3984     float total = 0.0;
3985     for(int i = (int)m_step; i < PRO_SYNC_TOTAL; i++) {
3986         total += m_syncUnits[i];
3987     }
3988     return total;
3989 }
3990
3991 float ProgressData::getDefaultUnits(ProgressStep step)
3992 {
3993     switch(step) {
3994         case PRO_SYNC_PREPARE:
3995             return PRO_SYNC_PREPARE_RATIO;
3996         case PRO_SYNC_INIT:
3997             return CONN_SETUP_RATIO + MSG_SEND_RECEIVE_TIMES;
3998         case PRO_SYNC_DATA:
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;
4002         default:
4003             return 0;
4004     };
4005 }
4006
4007 /************************ Connection implementation *****************/
4008
4009 void Connection::failed(const std::string &reason)
4010 {
4011     if (m_failure.empty()) {
4012         m_failure = reason;
4013         if (m_session) {
4014             m_session->setConnectionError(reason);
4015         }
4016     }
4017     if (m_state != FAILED) {
4018         abort();
4019     }
4020     m_state = FAILED;
4021 }
4022
4023 std::string Connection::buildDescription(const StringMap &peer)
4024 {
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");
4030     std::string buffer;
4031     buffer.reserve(256);
4032     if (desc != peer.end()) {
4033         buffer += desc->second;
4034     }
4035     if (id != peer.end() || trans != peer.end()) {
4036         if (!buffer.empty()) {
4037             buffer += " ";
4038         }
4039         buffer += "(";
4040         if (id != peer.end()) {
4041             buffer += id->second;
4042             if (trans != peer.end()) {
4043                 buffer += " via ";
4044             }
4045         }
4046         if (trans != peer.end()) {
4047             buffer += trans->second;
4048             if (trans_desc != peer.end()) {
4049                 buffer += " ";
4050                 buffer += trans_desc->second;
4051             }
4052         }
4053         buffer += ")";
4054     }
4055     return buffer;
4056 }
4057
4058 void Connection::wakeupSession()
4059 {
4060     if (m_loop) {
4061         g_main_loop_quit(m_loop);
4062         m_loop = NULL;
4063     }
4064 }
4065
4066 void Connection::process(const Caller_t &caller,
4067              const std::pair<size_t, const uint8_t *> &message,
4068              const std::string &message_type)
4069 {
4070     SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s sends %lu bytes via connection %s, %s",
4071                  caller.c_str(),
4072                  (unsigned long)message.first,
4073                  getPath(),
4074                  message_type.c_str());
4075
4076     boost::shared_ptr<Client> client(m_server.findClient(caller));
4077     if (!client) {
4078         throw runtime_error("unknown client");
4079     }
4080
4081     boost::shared_ptr<Connection> myself =
4082         boost::static_pointer_cast<Connection, Resource>(client->findResource(this));
4083     if (!myself) {
4084         throw runtime_error("client does not own connection");
4085     }
4086
4087     // any kind of error from now on terminates the connection
4088     try {
4089         switch (m_state) {
4090         case SETUP: {
4091             std::string config;
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),
4099                               message.first);
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) {
4108                         config = "default";
4109                         SE_LOG_DEBUG(NULL, NULL, "SAN parsing failed, falling back to 'default' config");
4110                     }  
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,
4119                             servers) {
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;
4125                                 break;
4126                             }
4127                         }
4128                         if (!config.empty()) {
4129                             break;
4130                         }
4131                     }
4132
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,
4140                                     servers) {
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;
4148                                         break;
4149                                     } 
4150                                 }
4151                                 if (!config.empty()){
4152                                     break;
4153                                 }
4154                             }
4155                         }
4156                     }
4157
4158                     if (config.empty()) {
4159                         BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server,
4160                                       servers) {
4161                             if (server.first == serverID) {
4162                                 config = serverID;
4163                                 break;
4164                             }
4165                         }
4166                     }
4167
4168                     // create a default configuration name if none matched
4169                     if (config.empty()) {
4170                         config = serverID+"_"+getCurrentTime();
4171                         SE_LOG_DEBUG(NULL,
4172                                      NULL,
4173                                      "SAN Server ID '%s' unknown, falling back to automatically created '%s' config",
4174                                      serverID.c_str(), config.c_str());
4175                     }
4176
4177
4178                     SE_LOG_DEBUG(NULL, NULL, "SAN sync with config %s", config.c_str());
4179
4180                     m_SANContent.reset (new SANContent ());
4181                     // extract number of sources
4182                     int numSources = san.fNSync;
4183                     int syncType;
4184                     uint32_t contentType;
4185                     std::string serverURI;
4186                     if (!numSources) {
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);
4193                         } else {
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());
4196                         }
4197                     } else {
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);
4203                             } else {
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);
4208                             }
4209                         }
4210                     }
4211                 }
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
4217                 serverMode = true;
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());
4222                 }
4223
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),
4228                                                          message.first,
4229                                                          message_type);
4230                 if (info.m_deviceID.empty()) {
4231                     // TODO: proper exception
4232                     throw runtime_error("could not extract LocURI=deviceID from initial message");
4233                 }
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());
4243                         break;
4244                     }
4245                 }
4246                 if (config.empty()) {
4247                     // TODO: proper exception
4248                     throw runtime_error(string("no configuration found for ") +
4249                                         info.toString());
4250                 }
4251
4252                 // abort previous session of this client
4253                 m_server.killSessions(info.m_deviceID);
4254                 peerDeviceID = info.m_deviceID;
4255             } else {
4256                 throw runtime_error(StringPrintf("message type '%s' not supported for starting a sync", message_type.c_str()));
4257             }
4258
4259             // run session as client or server
4260             m_state = PROCESSING;
4261             m_session = Session::createSession(m_server,
4262                                                peerDeviceID,
4263                                                config,
4264                                                m_sessionID);
4265             if (serverMode) {
4266                 m_session->initServer(SharedBuffer(reinterpret_cast<const char *>(message.second),
4267                                                    message.first),
4268                                       message_type);
4269             }
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);
4277             break;
4278         }
4279         case PROCESSING:
4280             throw std::runtime_error("protocol error: already processing a message");
4281             break;        
4282         case WAITING:
4283             m_incomingMsg = SharedBuffer(reinterpret_cast<const char *>(message.second),
4284                                          message.first);
4285             m_incomingMsgType = message_type;
4286             m_state = PROCESSING;
4287             // get out of DBusTransportAgent::wait()
4288             wakeupSession();
4289             break;
4290         case FINAL:
4291             wakeupSession();
4292             throw std::runtime_error("protocol error: final reply sent, no further message processing possible");
4293         case DONE:
4294             throw std::runtime_error("protocol error: connection closed, no further message processing possible");
4295             break;
4296         case FAILED:
4297             throw std::runtime_error(m_failure);
4298             break;
4299         default:
4300             throw std::runtime_error("protocol error: unknown internal state");
4301             break;
4302         }
4303     } catch (const std::exception &error) {
4304         failed(error.what());
4305         throw;
4306     } catch (...) {
4307         failed("unknown exception in Connection::process");
4308         throw;
4309     }
4310 }
4311
4312 void Connection::close(const Caller_t &caller,
4313                        bool normal,
4314                        const std::string &error)
4315 {
4316     SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s closes connection %s %s%s%s",
4317                  caller.c_str(),
4318                  getPath(),
4319                  normal ? "normally" : "with error",
4320                  error.empty() ? "" : ": ",
4321                  error.c_str());
4322
4323     boost::shared_ptr<Client> client(m_server.findClient(caller));
4324     if (!client) {
4325         throw runtime_error("unknown client");
4326     }
4327
4328     if (!normal ||
4329         m_state != FINAL) {
4330         std::string err = error.empty() ?
4331             "connection closed unexpectedly" :
4332             error;
4333         if (m_session) {
4334             m_session->setConnectionError(err);
4335         }
4336         failed(err);
4337     } else {
4338         m_state = DONE;
4339         if (m_session) {
4340             m_session->setConnectionError("");
4341         }
4342     }
4343
4344     // remove reference to us from client, will destruct *this*
4345     // instance!
4346     client->detach(this);
4347 }
4348
4349 void Connection::abort()
4350 {
4351     if (!m_abortSent) {
4352         sendAbort();
4353         m_abortSent = true;
4354         m_state = FAILED;
4355     }
4356 }
4357
4358 void Connection::shutdown()
4359 {
4360     // trigger removal of this connection by removing all
4361     // references to it
4362     m_server.detach(this);
4363 }
4364
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)),
4374     m_server(server),
4375     m_peer(peer),
4376     m_mustAuthenticate(must_authenticate),
4377     m_state(SETUP),
4378     m_sessionID(sessionID),
4379     m_loop(NULL),
4380     sendAbort(*this, "Abort"),
4381     m_abortSent(false),
4382     reply(*this, "Reply"),
4383     m_description(buildDescription(peer))
4384 {
4385     add(this, &Connection::process, "Process");
4386     add(this, &Connection::close, "Close");
4387     add(sendAbort);
4388     add(reply);
4389     m_server.autoTermRef();
4390 }
4391
4392 Connection::~Connection()
4393 {
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() ? "" : ": ",
4398                  m_failure.c_str());
4399     try {
4400         if (m_state != DONE) {
4401             abort();
4402         }
4403         // DBusTransportAgent waiting? Wake it up.
4404         wakeupSession();
4405         m_session.use_count();
4406         m_session.reset();
4407     } catch (...) {
4408         // log errors, but do not propagate them because we are
4409         // destructing
4410         Exception::handle();
4411     }
4412     m_server.autoTermUnref();
4413 }
4414
4415 void Connection::ready()
4416 {
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;
4429         }
4430         m_session->setConfig (false, false, from);
4431     }
4432     const SyncContext context (configName);
4433     std::list<std::string> sources = context.getSyncSources();
4434
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];
4443             bool found = false;
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;
4455                     found = true;
4456                     break;
4457                 }
4458             }
4459             if (!found) {
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());
4463             }
4464         }
4465         if (m_sourceModes.empty()) {
4466             SE_LOG_DEBUG(NULL, NULL,
4467                     "SAN message with no known entries, falling back to default");
4468             m_syncMode = "";
4469         }
4470     }
4471
4472     if (m_SANContent) {
4473         m_session->setRemoteInitiated(true);
4474     }
4475     // proceed with sync now that our session is ready
4476     m_session->sync(m_syncMode, m_sourceModes);
4477 }
4478
4479 /****************** DBusTransportAgent implementation **************/
4480
4481 DBusTransportAgent::DBusTransportAgent(GMainLoop *loop,
4482                                        Session &session,
4483                                        boost::weak_ptr<Connection> connection) :
4484     m_loop(loop),
4485     m_session(session),
4486     m_connection(connection),
4487     m_callback(NULL),
4488     m_eventTriggered(false),
4489     m_waiting(false)
4490 {
4491 }
4492
4493 DBusTransportAgent::~DBusTransportAgent()
4494 {
4495     boost::shared_ptr<Connection> connection = m_connection.lock();
4496     if (connection) {
4497         connection->shutdown();
4498     }
4499 }
4500
4501 void DBusTransportAgent::send(const char *data, size_t len)
4502 {
4503     boost::shared_ptr<Connection> connection = m_connection.lock();
4504
4505     if (!connection) {
4506         SE_THROW_EXCEPTION(TransportException,
4507                            "D-Bus peer has disconnected");
4508     }
4509
4510     if (connection->m_state != Connection::PROCESSING) {
4511         SE_THROW_EXCEPTION(TransportException,
4512                            "cannot send to our D-Bus peer");
4513     }
4514
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();
4519
4520     // setup regular callback
4521     if (m_callback) {
4522         m_eventSource = g_timeout_add_seconds(m_callbackInterval, timeoutCallback, static_cast<gpointer>(this));
4523     }
4524     m_eventTriggered = false;
4525
4526     // TODO: turn D-Bus exceptions into transport exceptions
4527     StringMap meta;
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);
4531 }
4532
4533 void DBusTransportAgent::shutdown()
4534 {
4535     boost::shared_ptr<Connection> connection = m_connection.lock();
4536
4537     if (!connection) {
4538         SE_THROW_EXCEPTION(TransportException,
4539                            "D-Bus peer has disconnected");
4540     }
4541
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),
4546                           "", StringMap(),
4547                           true, connection->m_sessionID);
4548     }
4549 }
4550
4551 gboolean DBusTransportAgent::timeoutCallback(gpointer transport)
4552 {
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);
4559     }
4560     return false;
4561 }
4562
4563 void DBusTransportAgent::doWait(boost::shared_ptr<Connection> &connection)
4564 {
4565     // let Connection wake us up when it has a reply or
4566     // when it closes down
4567     connection->m_loop = m_loop;
4568
4569     // release our reference so that the Connection instance can
4570     // be destructed when requested by the D-Bus peer
4571     connection.reset();
4572
4573     // now wait
4574     m_waiting = true;
4575     g_main_loop_run(m_loop);
4576     m_waiting = false;
4577 }
4578
4579 DBusTransportAgent::Status DBusTransportAgent::wait(bool noReply)
4580 {
4581     boost::shared_ptr<Connection> connection = m_connection.lock();
4582
4583     if (!connection) {
4584         SE_THROW_EXCEPTION(TransportException,
4585                            "D-Bus peer has disconnected");
4586     }
4587
4588     switch (connection->m_state) {
4589     case Connection::PROCESSING:
4590         m_incomingMsg = connection->m_incomingMsg;
4591         m_incomingMsgType = connection->m_incomingMsgType;
4592         return GOT_REPLY;
4593         break;
4594     case Connection::FINAL:
4595         if (m_eventTriggered) {
4596             return TIME_OUT;
4597         }
4598         doWait(connection);
4599
4600         // if the connection is still available, then keep waiting
4601         connection = m_connection.lock();
4602         if (connection) {
4603             return ACTIVE;
4604         } else if (m_session.getConnectionError().empty()) {
4605             return INACTIVE;
4606         } else {
4607             SE_THROW_EXCEPTION(TransportException, m_session.getConnectionError());
4608             return FAILED;
4609         }
4610         break;
4611     case Connection::WAITING:
4612         if (noReply) {
4613             // message is sent as far as we know, so return
4614             return INACTIVE;
4615         }
4616
4617         if (m_eventTriggered) {
4618             return TIME_OUT;
4619         }
4620         doWait(connection);
4621
4622         // tell caller to check again
4623         return ACTIVE;
4624         break;
4625     case Connection::DONE:
4626         if (!noReply) {
4627             SE_THROW_EXCEPTION(TransportException,
4628                                "internal error: transport has shut down, can no longer receive reply");
4629         }
4630         
4631         return CLOSED;
4632     default:
4633         SE_THROW_EXCEPTION(TransportException,
4634                            "internal error: send() on connection which is not ready");
4635         break;
4636     }
4637
4638     return FAILED;
4639 }
4640
4641 void DBusTransportAgent::getReply(const char *&data, size_t &len, std::string &contentType)
4642 {
4643     data = m_incomingMsg.get();
4644     len = m_incomingMsg.size();
4645     contentType = m_incomingMsgType;
4646 }
4647
4648 /********************* PresenceStatus implementation ****************/
4649 void PresenceStatus::init(){
4650     //initialize the configured peer list
4651     if (!m_initiated) {
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
4661                 PeerStatus status;
4662                 if ((boost::starts_with(url, "obex-bt") && m_btPresence) ||
4663                     (boost::starts_with (url, "http") && m_httpPresence)) {
4664                     status = MIGHTWORK;
4665                 } else {
4666                     status = NOTRANSPORT;
4667                 }
4668                 m_peers[server.first].push_back(make_pair(url, status));
4669             }
4670         }
4671         m_initiated = true;
4672     }
4673 }
4674
4675 /* Implement DBusServer::checkPresence*/
4676 void PresenceStatus::checkPresence (const string &peer, string& status, std::vector<std::string> &transport) {
4677
4678     if (!m_initiated) {
4679         //might triggered by updateConfigPeers
4680         init();
4681     }
4682
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);
4688         transport.clear();
4689         return;
4690     }
4691     PeerStatus mystatus = MIGHTWORK;
4692     transport.clear();
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);
4698         }
4699     }
4700     if (transport.empty()) {
4701         mystatus = NOTRANSPORT;
4702     }
4703     status = status2string(mystatus);
4704 }
4705
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;
4713     }
4714 }
4715
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);
4721     }else {
4722     }
4723 }
4724
4725 void PresenceStatus::updatePresenceStatus (bool httpPresence, bool btPresence) {
4726     bool httpChanged = (m_httpPresence != httpPresence);
4727     bool btChanged = (m_btPresence != btPresence);
4728     if(httpChanged) {
4729         m_httpTimer.reset();
4730     }
4731     if(btChanged) {
4732         m_btTimer.reset();
4733     }
4734
4735     if (m_initiated && !httpChanged && !btChanged) {
4736         //nothing changed
4737         return;
4738     }
4739
4740     //initialize the configured peer list using old presence status
4741     bool initiated = m_initiated;
4742     if (!m_initiated) {
4743         init();
4744     }
4745
4746     // switch to new status
4747     m_httpPresence = httpPresence;
4748     m_btPresence = btPresence;
4749
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",
4762                         peer.first.c_str(),
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",
4769                         peer.first.c_str(),
4770                         status2string (entry.second).c_str(), entry.first.c_str());
4771             }
4772         }
4773     }
4774 }
4775
4776 /********************** Connman Client implementation **************/
4777 ConnmanClient::ConnmanClient(DBusServer &server):
4778     m_server(server),
4779     m_propertyChanged(*this, "PropertyChanged")
4780 {
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);
4783     if (m_connmanConn){
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));
4788     }else{
4789         SE_LOG_ERROR (NULL, NULL, "DBus connection setup for connman failed");
4790     }
4791 }
4792
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());
4801             return;
4802         }
4803         SE_LOG_DEBUG (NULL, NULL, "error in connmanCallback %s", error.c_str());
4804         return;
4805     }
4806
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;
4817                     break;
4818                 }
4819             }
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")) {
4824                     btPresence = true;
4825                     break;
4826                 }
4827             }
4828         } else {
4829             continue;
4830         }
4831     }
4832     //now delivering the signals
4833     m_server.getPresenceStatus().updatePresenceStatus (httpPresence, btPresence);
4834 }
4835
4836 void ConnmanClient::propertyChanged(const string &name,
4837                                     const boost::variant<vector<string>, string> &prop)
4838 {
4839     bool httpPresence=false, btPresence=false;
4840     bool httpChanged=false, btChanged=false;
4841     if (boost::iequals(name, "ConnectedTechnologies")) {
4842         httpChanged=true;
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")) {
4847                 httpPresence=true;
4848                 break;
4849             }
4850         }
4851     } else if (boost::iequals (name, "AvailableTechnologies")){
4852         btChanged=true;
4853         vector<string> enabled = boost::get<vector<string> >(prop);
4854         BOOST_FOREACH (std::string tech, enabled){
4855             if (boost::iequals (tech, "bluetooth")) {
4856                 btPresence = true;
4857                 break;
4858             }
4859         }
4860     }
4861     if(httpChanged) {
4862         m_server.getPresenceStatus().updatePresenceStatus (httpPresence, PresenceStatus::HTTP_TRANSPORT);
4863     } else if (btChanged) {
4864         m_server.getPresenceStatus().updatePresenceStatus (btPresence, PresenceStatus::BT_TRANSPORT);
4865     } else {
4866     }
4867 }
4868
4869 /********************** DBusServer implementation ******************/
4870
4871 void DBusServer::clientGone(Client *c)
4872 {
4873     for(Clients_t::iterator it = m_clients.begin();
4874         it != m_clients.end();
4875         ++it) {
4876         if (it->second.get() == c) {
4877             SE_LOG_DEBUG(NULL, NULL, "D-Bus client %s has disconnected",
4878                          c->m_ID.c_str());
4879             autoTermUnref(it->second->getAttachCount());
4880             m_clients.erase(it);
4881             return;
4882         }
4883     }
4884     SE_LOG_DEBUG(NULL, NULL, "unknown client has disconnected?!");
4885 }
4886
4887 std::string DBusServer::getNextSession()
4888 {
4889     // Make the session ID somewhat random. This protects to
4890     // some extend against injecting unwanted messages into the
4891     // communication.
4892     m_lastSession++;
4893     if (!m_lastSession) {
4894         m_lastSession++;
4895     }
4896     return StringPrintf("%u%u", rand(), m_lastSession);
4897 }
4898
4899 vector<string> DBusServer::getCapabilities()
4900 {
4901     // Note that this is tested by test-dbus.py in
4902     // TestDBusServer.testCapabilities, update the test when adding
4903     // capabilities.
4904     vector<string> capabilities;
4905
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;
4913 }
4914
4915 StringMap DBusServer::getVersions()
4916 {
4917     StringMap versions;
4918
4919     versions["version"] = VERSION;
4920     versions["system"] = EDSAbiWrapperInfo();
4921     versions["backends"] = SyncSource::backendsInfo();
4922     return versions;
4923 }
4924
4925 void DBusServer::attachClient(const Caller_t &caller,
4926                               const boost::shared_ptr<Watch> &watch)
4927 {
4928     boost::shared_ptr<Client> client = addClient(getConnection(),
4929                                                  caller,
4930                                                  watch);
4931     autoTermRef();
4932     client->increaseAttachCount();
4933 }
4934
4935 void DBusServer::detachClient(const Caller_t &caller)
4936 {
4937     boost::shared_ptr<Client> client = findClient(caller);
4938     if (client) {
4939         autoTermUnref();
4940         client->decreaseAttachCount();
4941     }
4942 }
4943
4944 void DBusServer::setNotifications(bool enabled,
4945                                   const Caller_t &caller,
4946                                   const string & /* notifications */)
4947 {
4948     boost::shared_ptr<Client> client = findClient(caller);
4949     if (client && client->getAttachCount()) {
4950         client->setNotificationsEnabled(enabled);
4951     } else {
4952         SE_THROW("client not attached, not allowed to change notifications");
4953     }
4954 }
4955
4956 bool DBusServer::notificationsEnabled()
4957 {
4958     for(Clients_t::iterator it = m_clients.begin();
4959         it != m_clients.end();
4960         ++it) {
4961         if (!it->second->getNotificationsEnabled()) {
4962             return false;
4963         }
4964     }
4965     return true;
4966 }
4967
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)
4974 {
4975     if (!session.empty()) {
4976         // reconnecting to old connection is not implemented yet
4977         throw std::runtime_error("not implemented");
4978     }
4979     std::string new_session = getNextSession();
4980
4981     boost::shared_ptr<Connection> c(new Connection(*this,
4982                                                    getConnection(),
4983                                                    new_session,
4984                                                    peer,
4985                                                    must_authenticate));
4986     SE_LOG_DEBUG(NULL, NULL, "connecting D-Bus client %s with connection %s '%s'",
4987                  caller.c_str(),
4988                  c->getPath(),
4989                  c->m_description.c_str());
4990         
4991     boost::shared_ptr<Client> client = addClient(getConnection(),
4992                                                  caller,
4993                                                  watch);
4994     client->attach(c);
4995     c->activate();
4996
4997     object = c->getPath();
4998 }
4999
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)
5005 {
5006     boost::shared_ptr<Client> client = addClient(getConnection(),
5007                                                  caller,
5008                                                  watch);
5009     std::string new_session = getNextSession();   
5010     boost::shared_ptr<Session> session = Session::createSession(*this,
5011                                                                 "is this a client or server session?",
5012                                                                 server,
5013                                                                 new_session,
5014                                                                 flags);
5015     client->attach(session);
5016     session->activate();
5017     enqueue(session);
5018     object = session->getPath();
5019 }
5020
5021 void DBusServer::checkPresence(const std::string &server,
5022                                std::string &status,
5023                                std::vector<std::string> &transports)
5024 {
5025     return m_presence.checkPresence(server, status, transports);
5026 }
5027
5028 void DBusServer::getSessions(std::vector<DBusObject_t> &sessions)
5029 {
5030     sessions.reserve(m_workQueue.size() + 1);
5031     if (m_activeSession) {
5032         sessions.push_back(m_activeSession->getPath());
5033     }
5034     BOOST_FOREACH(boost::weak_ptr<Session> &session, m_workQueue) {
5035         boost::shared_ptr<Session> s = session.lock();
5036         if (s) {
5037             sessions.push_back(s->getPath());
5038         }
5039     }
5040 }
5041
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)),
5047     m_loop(loop),
5048     m_lastSession(time(NULL)),
5049     m_activeSession(NULL),
5050     m_lastInfoReq(0),
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"),
5058     m_presence(*this),
5059     m_connman(*this),
5060     m_autoSync(*this),
5061     m_autoTerm(m_autoSync.preventTerm() ? -1 : duration), //if there is any task in auto sync, prevent auto termination
5062     m_parentLogger(LoggerBase::instance())
5063 {
5064     struct timeval tv;
5065     gettimeofday(&tv, NULL);
5066     srand(tv.tv_usec);
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);
5086     add(configChanged);
5087     add(presence);
5088     add(infoRequest);
5089     add(logOutput);
5090
5091     LoggerBase::pushLogger(this);
5092     setLevel(LoggerBase::DEBUG);
5093 }
5094
5095 DBusServer::~DBusServer()
5096 {
5097     // make sure all other objects are gone before destructing ourselves
5098     m_syncSession.reset();
5099     m_workQueue.clear();
5100     m_clients.clear();
5101     LoggerBase::popLogger();
5102 }
5103
5104 void DBusServer::run()
5105 {
5106     while (!shutdownRequested) {
5107         if (!m_activeSession ||
5108             !m_activeSession->readyToRun()) {
5109             g_main_loop_run(m_loop);
5110         }
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();
5116             if (!session) {
5117                 throw runtime_error("internal error: session no longer available");
5118             }
5119             try {
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());
5125             } catch (...) {
5126                 SE_LOG_ERROR(NULL, NULL, "unknown error");
5127             }
5128             session.swap(m_syncSession);
5129             dequeue(session.get());
5130         } 
5131
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();
5136         }
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())
5142         {
5143             // if the autosync is the active session, then invoke 'sync'
5144             // to make it ready to run
5145             m_autoSync.prepare();
5146         }
5147     }
5148 }
5149
5150
5151 /**
5152  * look up client by its ID
5153  */
5154 boost::shared_ptr<Client> DBusServer::findClient(const Caller_t &ID)
5155 {
5156     for(Clients_t::iterator it = m_clients.begin();
5157         it != m_clients.end();
5158         ++it) {
5159         if (it->second->m_ID == ID) {
5160             return it->second;
5161         }
5162     }
5163     return boost::shared_ptr<Client>();
5164 }
5165
5166 boost::shared_ptr<Client> DBusServer::addClient(const DBusConnectionPtr &conn,
5167                                                 const Caller_t &ID,
5168                                                 const boost::shared_ptr<Watch> &watch)
5169 {
5170     boost::shared_ptr<Client> client(findClient(ID));
5171     if (client) {
5172         return client;
5173     }
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()));
5179     return client;
5180 }
5181
5182
5183 void DBusServer::detach(Resource *resource)
5184 {
5185     BOOST_FOREACH(const Clients_t::value_type &client_entry,
5186                   m_clients) {
5187         client_entry.second->detachAll(resource);
5188     }
5189 }
5190
5191 void DBusServer::enqueue(const boost::shared_ptr<Session> &session)
5192 {
5193     WorkQueue_t::iterator it = m_workQueue.end();
5194     while (it != m_workQueue.begin()) {
5195         --it;
5196         if (it->lock()->getPriority() <= session->getPriority()) {
5197             ++it;
5198             break;
5199         }
5200     }
5201     m_workQueue.insert(it, session);
5202
5203     checkQueue();
5204 }
5205
5206 int DBusServer::killSessions(const std::string &peerDeviceID)
5207 {
5208     int count = 0;
5209
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();
5219             if (c) {
5220                 c->shutdown();
5221             }
5222             it = m_workQueue.erase(it);
5223             count++;
5224         } else {
5225             ++it;
5226         }
5227     }
5228
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());
5234         try {
5235             // abort, even if not necessary right now
5236             m_activeSession->abort();
5237         } catch (...) {
5238             // TODO: catch only that exception which indicates
5239             // incorrect use of the function
5240         }
5241         dequeue(m_activeSession);
5242         count++;
5243     }
5244
5245     return count;
5246 }
5247
5248 void DBusServer::dequeue(Session *session)
5249 {
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.
5254         return;
5255     }
5256
5257     for (WorkQueue_t::iterator it = m_workQueue.begin();
5258          it != m_workQueue.end();
5259          ++it) {
5260         if (it->lock().get() == session) {
5261             // remove from queue
5262             m_workQueue.erase(it);
5263             // session was idle, so nothing else to do
5264             return;
5265         }
5266     }
5267
5268     if (m_activeSession == session) {
5269         // The session is releasing the lock, so someone else might
5270         // run now.
5271         session->setActive(false);
5272         sessionChanged(session->getPath(), false);
5273         m_activeSession = NULL;
5274         m_activeSessionRef.reset();
5275         checkQueue();
5276         return;
5277     }
5278 }
5279
5280 void DBusServer::checkQueue()
5281 {
5282     if (m_activeSession) {
5283         // still busy
5284         return;
5285     }
5286
5287     while (!m_workQueue.empty()) {
5288         boost::shared_ptr<Session> session = m_workQueue.front().lock();
5289         m_workQueue.pop_front();
5290         if (session) {
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);
5300             return;
5301         }
5302     }
5303 }
5304
5305 bool DBusServer::callTimeout(const boost::shared_ptr<Timeout> &timeout, const boost::function<bool ()> &callback)
5306 {
5307     if (!callback()) {
5308         m_timeouts.remove(timeout);
5309         return false;
5310     } else {
5311         return true;
5312     }
5313 }
5314
5315 void DBusServer::addTimeout(const boost::function<bool ()> &callback,
5316                             int seconds)
5317 {
5318     boost::shared_ptr<Timeout> timeout(new Timeout);
5319     m_timeouts.push_back(timeout);
5320     timeout->activate(seconds,
5321                       boost::bind(&DBusServer::callTimeout,
5322                                   this,
5323                                   // avoid copying the shared pointer here,
5324                                   // otherwise the Timeout will never be deleted
5325                                   boost::ref(m_timeouts.back()),
5326                                   callback));
5327 }
5328
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)
5333 {
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);
5339     }
5340 }
5341
5342 boost::shared_ptr<InfoReq> DBusServer::createInfoReq(const string &type,
5343                                                      const std::map<string, string> &parameters,
5344                                                      const Session *session)
5345 {
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));
5349     return infoReq;
5350 }
5351
5352 std::string DBusServer::getNextInfoReq()
5353 {
5354     return StringPrintf("%u", ++m_lastInfoReq);
5355 }
5356
5357 void DBusServer::emitInfoReq(const InfoReq &req)
5358 {
5359     infoRequest(req.getId(), 
5360                 req.getSessionPath(), 
5361                 req.getInfoStateStr(), 
5362                 req.getHandler(), 
5363                 req.getType(), 
5364                 req.getParam());
5365 }
5366
5367 void DBusServer::removeInfoReq(const InfoReq &req)
5368 {
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);
5373     }
5374 }
5375
5376 void DBusServer::getDeviceList(SyncConfig::DeviceList &devices)
5377 {
5378     //wait bluez or other device managers
5379     while(!m_bluezManager.isDone()) {
5380         g_main_loop_run(m_loop);
5381     }
5382
5383     devices.clear();
5384     devices = m_syncDevices;
5385 }
5386
5387 void DBusServer::addPeerTempl(const string &templName, 
5388                               const boost::shared_ptr<SyncConfig::TemplateDescription> peerTempl)
5389 {
5390     std::string lower = templName;
5391     boost::to_lower(lower);
5392     m_matchedTempls.insert(MatchedTemplates::value_type(lower, peerTempl));
5393 }
5394
5395 boost::shared_ptr<SyncConfig::TemplateDescription> DBusServer::getPeerTempl(const string &peer)
5396 {
5397     std::string lower = peer;
5398     boost::to_lower(lower);
5399     MatchedTemplates::iterator it = m_matchedTempls.find(lower);
5400     if(it != m_matchedTempls.end()) {
5401         return it->second;
5402     } else {
5403         return boost::shared_ptr<SyncConfig::TemplateDescription>();
5404     }
5405 }
5406
5407 bool DBusServer::getDevice(const string &deviceId, SyncConfig::DeviceDescription &device)
5408 {
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;
5413             return true;
5414         }
5415     }
5416     return false;
5417 }
5418
5419 void DBusServer::addDevice(const SyncConfig::DeviceDescription &device)
5420 {
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)) {
5424             break;
5425         }
5426     }
5427     if(it == m_syncDevices.end()) {
5428         m_syncDevices.push_back(device);
5429         templatesChanged();
5430     }
5431 }
5432
5433 void DBusServer::removeDevice(const string &deviceId)
5434 {
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);
5439             templatesChanged();
5440             break;
5441         }
5442     }
5443 }
5444
5445 void DBusServer::updateDevice(const string &deviceId,
5446                               const SyncConfig::DeviceDescription &device)
5447 {
5448     SyncConfig::DeviceList::iterator it;
5449     for(it = m_syncDevices.begin(); it != m_syncDevices.end(); ++it) {
5450         if(boost::iequals(it->m_deviceId, deviceId)) {
5451             (*it) = device; 
5452             templatesChanged();
5453             break;
5454         }
5455     }
5456 }
5457
5458 void DBusServer::messagev(Level level,
5459                           const char *prefix,
5460                           const char *file,
5461                           int line,
5462                           const char *function,
5463                           const char *format,
5464                           va_list args)
5465 {
5466     // iterating over args in messagev() is destructive, must make a copy first
5467     va_list argsCopy;
5468     va_copy(argsCopy, args);
5469     m_parentLogger.messagev(level, prefix, file, line, function, format, args);
5470     string log = StringPrintfV(format, argsCopy);
5471     va_end(argsCopy);
5472
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);
5479     } else {
5480         logOutput(getPath(), strLevel, log);
5481     }
5482 }
5483
5484 /********************** InfoReq implementation ******************/
5485 InfoReq::InfoReq(DBusServer &server,
5486                  const string &type,
5487                  const InfoMap &parameters,
5488                  const Session *session,
5489                  uint32_t timeout) :
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)
5493 {
5494     m_id = m_server.getNextInfoReq();
5495     m_server.emitInfoReq(*this);
5496     m_param.clear();
5497 }
5498
5499 InfoReq::~InfoReq()
5500 {
5501     m_handler = "";
5502     done();
5503     m_server.removeInfoReq(*this);
5504 }
5505
5506 InfoReq::Status InfoReq::check()
5507 {
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);
5511         checkTimeout();
5512     }
5513     return m_status;
5514 }
5515
5516 bool InfoReq::getResponse(InfoMap &response)
5517 {
5518     if (m_status == ST_OK) {
5519         response = m_response;
5520         return true;
5521     }
5522     return false;
5523 }
5524
5525 InfoReq::Status InfoReq::wait(InfoMap &response, uint32_t interval)
5526 {
5527     // give a chance to check whether it has been timeout
5528     check();
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);
5535         }
5536
5537         // if the source is not removed
5538         if(m_status != ST_TIMEOUT && m_status != ST_CANCEL) {
5539             g_source_remove(checkSource);
5540         }
5541     }
5542     if (m_status == ST_OK) {
5543         response = m_response;
5544     }
5545     return m_status;
5546 }
5547
5548 void InfoReq::cancel()
5549 {
5550     if(m_status == ST_RUN) {
5551         m_handler = "";
5552         done();
5553         m_status = ST_CANCEL;
5554     }
5555 }
5556
5557 string InfoReq::statusToString(Status status)
5558 {
5559     switch(status) {
5560     case ST_RUN:
5561         return "running";
5562     case ST_OK:
5563         return "ok";
5564     case ST_CANCEL:
5565         return "cancelled";
5566     case ST_TIMEOUT:
5567         return "timeout";
5568     default:
5569         return "";
5570     };
5571 }
5572
5573 string InfoReq::infoStateToString(InfoState state)
5574 {
5575     switch(state) {
5576     case IN_REQ:
5577         return "request";
5578     case IN_WAIT:
5579         return "waiting";
5580     case IN_DONE:
5581         return "done";
5582     default:
5583         return "";
5584     }
5585 }
5586
5587 gboolean InfoReq::checkCallback(gpointer data)
5588 {
5589     // TODO: check abort and suspend(MB#8730)
5590
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()) {
5595         return FALSE;
5596     }
5597     return TRUE;
5598 }
5599
5600 bool InfoReq::checkTimeout()
5601 {
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;
5606             return true;
5607         }
5608     }
5609     return false;
5610 }
5611
5612 void InfoReq::setResponse(const Caller_t &caller, const string &state, const InfoMap &response)
5613 {
5614     if(m_status != ST_RUN) {
5615         return;
5616     } else if(m_infoState == IN_REQ && state == "working") {
5617         m_handler = caller;
5618         m_infoState = IN_WAIT;
5619         m_server.emitInfoReq(*this);
5620         //reset the timer, used to check timeout
5621         m_timer.reset();
5622     } else if(m_infoState == IN_WAIT && state == "response") {
5623         m_response = response;
5624         m_handler = caller;
5625         done();
5626         m_status = ST_OK;
5627     }
5628 }
5629
5630 void InfoReq::done()
5631 {
5632     if (m_infoState != IN_DONE) {
5633         m_infoState = IN_DONE;
5634         m_server.emitInfoReq(*this);
5635     }
5636 }
5637
5638 /********************** BluezManager implementation ******************/
5639 BluezManager::BluezManager(DBusServer &server) :
5640     m_server(server),
5641     m_adapterChanged(*this, "DefaultAdapterChanged")
5642 {
5643     m_bluezConn = b_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, true, NULL);
5644     if(m_bluezConn) {
5645         m_done = false;
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));
5649     } else {
5650         m_done = true;
5651     }
5652 }
5653
5654 void BluezManager::defaultAdapterChanged(const DBusObject_t &adapter)
5655 {
5656     m_done = false;
5657     //remove devices that belong to this original adapter
5658     if(m_adapter) {
5659         BOOST_FOREACH(boost::shared_ptr<BluezDevice> &device, m_adapter->getDevices()) {
5660             m_server.removeDevice(device->getMac());
5661         }
5662     }
5663     string error;
5664     defaultAdapterCb(adapter, error);
5665 }
5666
5667 void BluezManager::defaultAdapterCb(const DBusObject_t &adapter, const string &error)
5668 {
5669     if(!error.empty()) {
5670         SE_LOG_DEBUG (NULL, NULL, "Error in calling DefaultAdapter of Interface org.bluez.Manager: %s", error.c_str());
5671         m_done = true;
5672         return;
5673     }
5674     m_adapter.reset(new BluezAdapter(*this, adapter)); 
5675 }
5676
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")
5680 {
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));
5685 }
5686
5687 void BluezManager::BluezAdapter::listDevicesCb(const std::vector<DBusObject_t> &devices, const string &error)
5688 {
5689     if(!error.empty()) {
5690         SE_LOG_DEBUG (NULL, NULL, "Error in calling ListDevices of Interface org.bluez.Adapter: %s", error.c_str());
5691         checkDone(true);
5692         return;
5693     }
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);
5698     }
5699     checkDone();
5700 }
5701
5702 void BluezManager::BluezAdapter::deviceRemoved(const DBusObject_t &object)
5703 {
5704     string address;
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) {
5710                 m_devReplies--;
5711             }
5712             m_devNo--;
5713             m_devices.erase(devIt);
5714             break;
5715         }
5716     }
5717     m_manager.m_server.removeDevice(address);
5718 }
5719
5720 void BluezManager::BluezAdapter::deviceCreated(const DBusObject_t &object)
5721 {
5722     m_devNo++;
5723     boost::shared_ptr<BluezDevice> bluezDevice(new BluezDevice(*this, object));
5724     m_devices.push_back(bluezDevice);
5725 }
5726
5727 BluezManager::BluezDevice::BluezDevice (BluezAdapter &adapter, const string &path)
5728     : m_adapter(adapter), m_path(path), m_reply(false), m_propertyChanged(*this, "PropertyChanged")
5729 {
5730     DBusClientCall1<PropDict> getProperties(*this, "GetProperties");
5731     getProperties(boost::bind(&BluezDevice::getPropertiesCb, this, _1, _2));
5732
5733     m_propertyChanged.activate(boost::bind(&BluezDevice::propertyChanged, this, _1, _2));
5734 }
5735
5736 void BluezManager::BluezDevice::checkSyncService(const std::vector<std::string> &uuids)
5737 {
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));
5747             }
5748             break;
5749         }
5750     }
5751     // if sync service is not available now, possible to remove device
5752     if(!hasSyncService && !m_mac.empty()) {
5753         server.removeDevice(m_mac);
5754     }
5755 }
5756
5757 void BluezManager::BluezDevice::getPropertiesCb(const PropDict &props, const string &error)
5758 {
5759     m_adapter.m_devReplies++;
5760     m_reply = true;
5761     if(!error.empty()) {
5762         SE_LOG_DEBUG (NULL, NULL, "Error in calling GetProperties of Interface org.bluez.Device: %s", error.c_str());
5763     } else {
5764         PropDict::const_iterator it = props.find("Name");
5765         if(it != props.end()) {
5766             m_name = boost::get<string>(it->second);
5767         }
5768         it = props.find("Address");
5769         if(it != props.end()) {
5770             m_mac = boost::get<string>(it->second);
5771         }
5772
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);
5777         }
5778     }
5779     m_adapter.checkDone();
5780 }
5781
5782 void BluezManager::BluezDevice::propertyChanged(const string &name,
5783                                                 const boost::variant<vector<string>, string> &prop)
5784 {
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);
5792         }
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);
5802         }
5803         m_mac = mac;
5804     }
5805 }
5806
5807 /************************ AutoSyncManager ******************/
5808 void AutoSyncManager::init()
5809 {
5810     m_peerMap.clear();
5811     SyncConfig::ConfigList list = SyncConfig::getConfigs();
5812     BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server, list) {
5813         initConfig(server.first);
5814     }
5815 }
5816
5817 void AutoSyncManager::initConfig(const string &configName)
5818 {
5819     SyncConfig config (configName);
5820     if(!config.exists()) {
5821         return;
5822     }
5823     vector<string> urls = config.getSyncURL();
5824     string autoSync = config.getAutoSync();
5825
5826     //enable http and bt?
5827     bool http = false, bt = false;
5828     if(autoSync.empty() || boost::iequals(autoSync, "0")
5829             || boost::iequals(autoSync, "f")) {
5830         http = false;
5831         bt = false;
5832     } else if(boost::iequals(autoSync, "1") || boost::iequals(autoSync, "t")) {
5833         http = true;
5834         bt = true;
5835     } else {
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")) {
5840                 http = true;
5841             } else if(boost::iequals(op, "obex-bt")) {
5842                 bt = true;
5843             }
5844         }
5845     }
5846
5847     unsigned int interval = config.getAutoSyncInterval();
5848     unsigned int duration = config.getAutoSyncDelay();
5849
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);
5857             } else {
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));
5862             }
5863         }
5864     }
5865 }
5866
5867 void AutoSyncManager::remove(const string &configName)
5868 {
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);
5877             } else {
5878                 ++taskIt;
5879             }
5880         }
5881         //if list is empty, remove the list from map
5882         if(list->empty()) {
5883             PeerMap::iterator erased = it++;
5884             m_peerMap.erase(erased);
5885         } else {
5886             ++it;
5887         }
5888     }
5889
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);
5895         } else {
5896             ++qit;
5897         }
5898     }
5899 }
5900
5901 void AutoSyncManager::update(const string &configName)
5902 {
5903     // remove task from m_peerMap and tasks in the working queue for this config
5904     remove(configName);
5905     // re-load the config and re-init peer map
5906     initConfig(configName);
5907
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());
5912         m_session.reset();
5913         m_activeTask.reset();
5914         startTask();
5915     }
5916 }
5917
5918 void AutoSyncManager::scheduleAll()
5919 {
5920     BOOST_FOREACH(PeerMap::value_type &elem, m_peerMap) {
5921         elem.second->scheduleTaskList();
5922     }
5923 }
5924
5925 bool AutoSyncManager::addTask(const AutoSyncTask &syncTask)
5926 {
5927     if(taskLikelyToRun(syncTask)) {
5928         m_workQueue.push_back(syncTask);
5929         return true;
5930     }
5931     return false;
5932 }
5933
5934 bool AutoSyncManager::findTask(const AutoSyncTask &syncTask)
5935 {
5936     if(m_activeTask && *m_activeTask == syncTask) {
5937         return true;
5938     }
5939     BOOST_FOREACH(const AutoSyncTask &task, m_workQueue) {
5940         if(task == syncTask) {
5941             return true;
5942         }
5943     }
5944     return false;
5945 }
5946
5947 bool AutoSyncManager::taskLikelyToRun(const AutoSyncTask &syncTask)
5948 {
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()) {
5952         return false;
5953     }
5954
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',
5960             // then return true
5961             if (timer.timeout(syncTask.m_delay * 1000 /* seconds to milliseconds */)) {
5962                 return true;
5963             }
5964         } 
5965     } else if (boost::istarts_with(syncTask.m_url, "obex-bt") && status.getBtPresence()) {
5966         // don't add duplicate tasks
5967         if(!findTask(syncTask)) {
5968             return true;
5969         }
5970     }
5971     return false;
5972 }
5973
5974 void AutoSyncManager::startTask()
5975 {
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,
5983                                            "",
5984                                            m_activeTask->m_peer,
5985                                            newSession);
5986         m_session->setPriority(Session::PRI_AUTOSYNC);
5987         m_session->addListener(this);
5988         m_server.enqueue(m_session);
5989     }
5990 }
5991
5992 bool AutoSyncManager::hasActiveSession()
5993 {
5994     return m_session && m_session->getActive();
5995 }
5996
5997 void AutoSyncManager::prepare()
5998 {
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);
6007
6008         string mode;
6009         Session::SourceModes_t sourceModes;
6010         m_session->sync(mode, sourceModes);
6011     }
6012 }
6013
6014 void AutoSyncManager::syncSuccessStart()
6015 {
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());
6018 #ifdef HAS_NOTIFY
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());
6024     }
6025 #endif
6026 }
6027
6028 void AutoSyncManager::syncDone(SyncMLStatus status)
6029 {
6030     SE_LOG_INFO(NULL, NULL,"Automatic sync for '%s' has been done.\n", m_activeTask->m_peer.c_str());
6031 #ifdef HAS_NOTIFY
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());
6047         }
6048     }
6049 #endif
6050     m_session.reset();
6051     m_activeTask.reset();
6052     m_syncSuccessStart = false;
6053 }
6054
6055 #ifdef HAS_NOTIFY
6056 AutoSyncManager::Notification::Notification()
6057 {
6058     bindtextdomain (GETTEXT_PACKAGE, SYNCEVOLUTION_LOCALEDIR);
6059     bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
6060     textdomain (GETTEXT_PACKAGE);
6061     m_init = notify_init("SyncEvolution");
6062     m_actions = false;
6063     m_notification = NULL;
6064     // check whether 'actions' are supported by notification server 
6065     if(m_init) {
6066         GList *list = notify_get_server_caps();
6067         if(list) {
6068             for(; list != NULL; list = list->next) {
6069                 if(boost::iequals((char *)list->data, "actions")) {
6070                     m_actions = true;
6071                 }
6072             }
6073         }
6074     }
6075 }
6076
6077 AutoSyncManager::Notification::~Notification()
6078 {
6079     if(m_init) {
6080         notify_uninit();
6081     }
6082 }
6083
6084 void AutoSyncManager::Notification::notifyAction(NotifyNotification *notify,
6085                                                  gchar *action,
6086                                                  gpointer userData)
6087 {
6088     if(boost::iequals("view", action)) {
6089         pid_t pid;
6090         if((pid = fork()) == 0) {
6091             //search sync-ui from $PATH
6092             if(execlp("sync-ui", "sync-ui", (const char*)0) < 0) {
6093                 exit(0);
6094             }
6095         }
6096     } 
6097     //if dismiss, ignore
6098 }
6099
6100 void AutoSyncManager::Notification::send(const char *summary,
6101                                          const char *body,
6102                                          const char *viewParams)
6103 {
6104     if(!m_init)
6105         return;
6106
6107     if(m_notification) {
6108         notify_notification_clear_actions(m_notification);
6109         notify_notification_close(m_notification, NULL);
6110     }
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
6117     if(m_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);
6123     }
6124     notify_notification_show(m_notification, NULL);
6125 }
6126 #endif
6127
6128 void AutoSyncManager::AutoSyncTaskList::createTimeoutSource()
6129 {
6130     //if interval is 0, only run auto sync when changes are detected.
6131     if(m_interval) {
6132         m_source = g_timeout_add_seconds(m_interval, taskListTimeoutCb, static_cast<gpointer>(this));
6133     }
6134 }
6135
6136 gboolean AutoSyncManager::AutoSyncTaskList::taskListTimeoutCb(gpointer data)
6137 {
6138     AutoSyncTaskList *list = static_cast<AutoSyncTaskList*>(data);
6139     list->scheduleTaskList();
6140     return TRUE;
6141 }
6142
6143 void AutoSyncManager::AutoSyncTaskList::scheduleTaskList()
6144 {
6145     BOOST_FOREACH(AutoSyncTask &syncTask, *this) {
6146         m_manager.addTask(syncTask);
6147     }
6148     g_main_loop_quit(m_manager.m_server.getLoop());
6149 }
6150
6151 /**************************** main *************************/
6152
6153 void niam(int sig)
6154 {
6155     shutdownRequested = true;
6156     SyncContext::handleSignal(sig);
6157     g_main_loop_quit (loop);
6158 }
6159
6160 static bool parseDuration(int &duration, const char* value)
6161 {
6162     if(value == NULL) {
6163         return false;
6164     } else if (boost::iequals(value, "unlimited")) {
6165         duration = -1;
6166         return true;
6167     } else if ((duration = atoi(value)) > 0) {
6168         return true;
6169     } else {
6170         return false;
6171     }
6172 }
6173
6174 int main(int argc, char **argv)
6175 {
6176     int duration = 600;
6177     int opt = 1;
6178     while(opt < argc) {
6179         if(argv[opt][0] != '-') {
6180             break;
6181         }
6182         if (boost::iequals(argv[opt], "--duration") ||
6183             boost::iequals(argv[opt], "-d")) {
6184             opt++;
6185             if(!parseDuration(duration, opt== argc ? NULL : argv[opt])) {
6186                 std::cout << argv[opt-1] << ": unknown parameter value or not set" << std::endl;
6187                 return false;
6188             }
6189         } else {
6190             std::cout << argv[opt] << ": unknown parameter" << std::endl;
6191             return false;
6192         }
6193         opt++;
6194     }
6195     try {
6196         g_type_init();
6197         g_thread_init(NULL);
6198         g_set_application_name("SyncEvolution");
6199
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();
6206
6207         loop = g_main_loop_new (NULL, FALSE);
6208
6209         setvbuf(stderr, NULL, _IONBF, 0);
6210         setvbuf(stdout, NULL, _IONBF, 0);
6211
6212         signal(SIGTERM, niam);
6213         signal(SIGINT, niam);
6214
6215         LogRedirect redirect(true);
6216         redirectPtr = &redirect;
6217
6218         // make daemon less chatty - long term this should be a command line option
6219         LoggerBase::instance().setLevel(LoggerBase::INFO);
6220
6221         DBusErrorCXX err;
6222         DBusConnectionPtr conn = b_dbus_setup_bus(DBUS_BUS_SESSION,
6223                                                   "org.syncevolution",
6224                                                   true,
6225                                                   &err);
6226         if (!conn) {
6227             err.throwFailure("b_dbus_setup_bus()", " failed - server already running?");
6228         }
6229
6230         DBusServer server(loop, conn, duration);
6231         server.activate();
6232
6233         SE_LOG_INFO(NULL, NULL, "%s: ready to run",  argv[0]);
6234         server.run();
6235         return 0;
6236     } catch ( const std::exception &ex ) {
6237         SE_LOG_ERROR(NULL, NULL, "%s", ex.what());
6238     } catch (...) {
6239         SE_LOG_ERROR(NULL, NULL, "unknown error");
6240     }
6241
6242     return 1;
6243 }